T1
题目大意
有一个 1010010^{100}10100 行 1010010^{100}10100 列的网格.
输入 P,Q,X,YP, Q, X, YP,Q,X,Y, 首先你需要将 (P,Q)(P, Q)(P,Q) 为左上角的 100×100100\times 100100×100 的正方形包括的位置染成黑色. 判断 (X,Y)(X, Y)(X,Y) 是否被染成了黑色.
思路
区间判断即可.
当 P≤X<P+100 and Q≤Y<Q+100P\leq X < P + 100 \text{ and } Q\leq Y < Q + 100P≤X<P+100 and Q≤Y<Q+100 时输出 Yes 即可.
cpp
int main()
{
int X, Y, P, Q;
scanf("%d %d %d %d", &P, &Q, &X, &Y);
if (P <= X && X <= P + 99 && Q <= Y && Y <= Q + 99) puts("Yes");
else puts("No");
return 0;
}
T2
题目大意
有两种语言, 每个语言都有一组特定的小写字母, 有 QQQ 个询问, 让你判断输入的字符串是哪种语言, 两种都可能是或两种都不是输出 Unknown.
思路
模拟判断即可.
cpp
int N, M;
char str1[128], str2[128];
int vis1[256], vis2[256];
int main()
{
scanf("%d %d\n", &N, &M);
scanf("%s", str1);
scanf("%s", str2);
for (int i = 0; i < N; i++) vis1[(int) str1[i]] = 1;
for (int i = 0; i < M; i++) vis2[(int) str2[i]] = 1;
int Q;
scanf("%d", &Q);
while (Q--) {
char q[128] = {0};
scanf("%s", q);
int flag1 = 1, flag2 = 1;
int len = strlen(q);
for (int i = 0; i < len; i++) {
if (!vis1[(int) q[i]]) flag1 = 0;
if (!vis2[(int) q[i]]) flag2 = 0;
}
if (flag1 && !flag2) printf("Takahashi\n");
else if (!flag1 && flag2) printf("Aoki\n");
else printf("Unknown\n");
}
return 0;
}
T3
题目大意
有 NNN 个瓶子, 每个瓶子里面有 AiA_iAi mL 液体.
瓶子里可能出现两种液体: 水和烧酒.
现在我们不知道瓶子里是什么液体, 但是我们知道刚好有 KKK 瓶是烧酒, 问在最坏的情况, 你至少 要喝多少瓶才能喝到 XXX mL 烧酒.
思路
考虑到最坏的情况, 这 KKK 瓶烧酒刚好就是最少的瓶子里面的, 我们要选的最少, 自然是每次选择最大的一瓶烧酒, 按照题意排序模拟即可, 时间复杂度 O(NlogN).O(N\log N).O(NlogN).
cpp
#define MAXN 300010
long long a[MAXN];
long long q = 0;
int main()
{
int n, k;
scanf("%d %d %lld", &n, &k, &q);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
sort(a + 1, a + n + 1);
long long sum = 0;
for (int i = 1; i <= k; i++) sum += a[i];
if (sum < q) return printf("-1"), 0;
sum = 0;
int ans = n - k;
for (int i = k; i >= 1; i--) {
sum += a[i];
ans++;
if (sum >= q) return printf("%d", ans), 0;
}
// printf("%d", ans);
return 0;
}
T4
题目大意
有一个 NNN 和节点 MMM 条边的有向图, 每个边都有一个权值 CiC_iCi, 现在我们知道, 这个图的每个点的出边最多为 444, 我们现在要求满足这样条件的节点:
- 这个点可以从节点 111 走到, 而且路径长度要刚好为 LLL.(可以走重复的点和边.)
- 这条路径上面的点权 CiC_iCi 之和要在 [S,T][S, T][S,T] 这个区间里面.
形式化的讲:
有一个 NNN 个节点 MMM 条边的有向图, 每个边连接点 Ui,ViU_i,V_iUi,Vi 权值为 CiC_iCi, 我们现在要找这样的节点:(设这个节点为 PPP)
- 设从节点 111 到节点 PPP 存在长度为 LLL 的路径.
- 这些路径中的任意一条, 我们设这条路径上面的点为 N1,N2,...,NLN_1,N_2,...,N_LN1,N2,...,NL(Np=NqN_p=N_qNp=Nq是允许的). 如果 ∑i=1LNi∈[S,T]\sum_{i=1}^{L}N_i\in[S,T]∑i=1LNi∈[S,T], 那么这个点就是合法的.
思路
因为 L≤10L\leq 10L≤10, 所以我们可以直接暴搜.
因为每个点的出边最多为 444, 410≈1064^{10}\approx10^6410≈106 不会超时.
cpp
int N, M, L, S, T;
const int MAXN = 2e5 + 10;
set<int> st;
vector<pair<int, int>> G[MAXN];
int vis[MAXN];
void dfs(int u, int sum, int l) {
if (l == L) {
if (S <= sum && sum <= T) st.insert(u);
return ;
}
for (auto [v, w] : G[u]) dfs(v, sum + w, l + 1);
}
int main()
{
scanf("%d %d %d %d %d", &N, &M, &L, &S, &T);
for (int i = 1; i <= M; i++) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
G[x].push_back({y, z});
}
dfs(1, 0, 0);
for (int it : st) printf("%d ", it);
return 0;
}
T5
题目描述
有一个长度为 NNN 的字符串 SSS, 我们要统计满足一下条件的子串个数:
设这个子串 A 的个数为 PPP, B 的个数为 QQQ. 如果这个字符串满足 P>QP>QP>Q, 那么就满足条件.
思路
这里 NNN 是 5×1055\times 10^55×105 的级别. 我们好好考虑该怎么解决这个问题.
我们使用两个前缀和数组 ai,bia_i,b_iai,bi.
- aia_iai 表示从 111 到 iii 中
A的个数. - bib_ibi 表示从 111 到 iii 中
B的个数.
那么子串 Sl,Sl+1,...,SrS_l,S_{l+1},...,S_rSl,Sl+1,...,Sr 的 A 和 B 的个数分别为:
A的个数就是 ar−al−1a_r-a_{l-1}ar−al−1.B的个数就是 br−bl−1b_r-b_{l-1}br−bl−1.
我们发现满足条件的字符串其实就是:
ar−al−1>br−bl−1a_r-a_{l-1}>b_r-b_{l-1}ar−al−1>br−bl−1
移一下项
ar−br>al−1−bl−1a_r-b_r>a_{l-1}-b_{l-1}ar−br>al−1−bl−1
发现统计这个可以使用树状数组, 边操作边统计答案, 时间复杂度为 O(NlogN)O(N\log N)O(NlogN)
注意减法可能出现负数, 可以考虑加上一个大的常数.
cpp
const int MAXN = 5e5 + 10;
int N;
char str[MAXN];
// 树状数组
int sum[MAXN << 3];
void add(int pos, int val) {
for (; pos <= (MAXN << 1); pos += pos & -pos) sum[pos] += val;
}
int get_sum(int pos) {
int ret = 0;
for (; pos > 1; pos -= pos & -pos) ret += sum[pos];
return ret;
}
int a[MAXN], b[MAXN];
int main()
{
scanf("%d", &N);
scanf("%s", str + 1);
for (int i = 1; i <= N; i++) {
a[i] = a[i - 1] + (str[i] == 'A');
b[i] = b[i - 1] + (str[i] == 'B');
}
long long ans = 0;
for (int i = 1; i <= N; i++) {
int id = a[i] - b[i] + MAXN;
add(a[i - 1] - b[i - 1] + MAXN, 1);
ans += get_sum(id - 1);
}
printf("%lld\n", ans);
return 0;
}
T6
赛时差一点就想出来了.
题目描述
这里不想翻译了, 可以自己查看
思路
考虑分析如何使用 dp 解决这个问题.
我们设 dp1i,jdp1_{i,j}dp1i,j 表示考虑前 iii 个物品, 总重量最大为 jjj 时的最大价值.
再设 dp2i,jdp2_{i,j}dp2i,j 表示考虑第 iii 个物品到第 NNN 个物品, 总重量最大为 jjj 时的最大价值.
这两个数组就是 0/1 背包, 时间复杂度为 O(NM)O(NM)O(NM)
那么最大价值一定就是 dp1N,Mdp1_{N,M}dp1N,M, 我们设这个为 SSS.
我们一个一个考虑: (设当前的物品编号为 iii)
对于
A的情况.我们发现我们可以考虑不买这个东西, 看看是不是对于所有的 jjj, 都得不到最优的解
那么在这里, 不考虑这个东西的价值就是 dp1i−1,j+dp2i+1,M−jdp1_{i-1,j}+dp2_{i+1,M-j}dp1i−1,j+dp2i+1,M−j.
对于C的情况.我们可以考虑买下这个东西, 看看是不是能得到最优解.
那么买了这个东西的总重量就变成了 M−PiM-P_iM−Pi, 同时我们也要增加 ViV_iVi.
那么我们再使用 dp 数组: 价值就是 dp1i−1,j+dp2i+1,M−Pi−j+Vidp1_{i-1,j}+dp2_{i+1,M-P_i-j}+V_idp1i−1,j+dp2i+1,M−Pi−j+Vi.
那么剩下的情况就是B了.
这个步骤需要枚举 i,ji,ji,j 时间复杂度为 O(NM)O(NM)O(NM) 前一个步骤也是 O(NM)O(NM)O(NM).
所以总的时间复杂度为 O(NM)O(NM)O(NM) 空间复杂度为 O(NM)O(NM)O(NM).
cpp
typedef long long LL;
const int MAXN = 1024;
const int MAXM = 5e4 + 10;
LL dp1[MAXN][MAXM], dp2[MAXN][MAXM];
int N, M;
int P[MAXN], V[MAXN];
template <class T> inline T max(const T& x, const T& y) {return x > y ? x : y;}
template <class T> inline T min(const T& x, const T& y) {return x < y ? x : y;}
signed main()
{
scanf("%d %d", &N, &M);
for (int i = 1; i <= N; i++) scanf("%d %d", &P[i], &V[i]);
for (int i = 1; i <= N; i++)
for (int j = 0; j <= M; j++) {
dp1[i][j] = dp1[i - 1][j];
if (j >= P[i]) dp1[i][j] = max(dp1[i][j], dp1[i - 1][j - P[i]] + V[i]);
}
for (int i = N; i >= 1; i--) {
for (int j = M; j >= 1; j--) {
dp2[i][j] = dp2[i + 1][j];
if (j >= P[i]) dp2[i][j] = max(dp2[i][j], dp2[i + 1][j - P[i]] + V[i]);
}
}
LL ans = dp1[N][M];
for (int i = 1; i <= N; i++) {
// 考虑是否为 C 类物品
int isC = 1;
LL W = M - P[i];
LL cmp = ans - V[i];
for (int j = 0; j <= W; j++) if (dp1[i - 1][j] + dp2[i + 1][W - j] >= cmp) {isC = 0; break;}
if (isC) {putchar('C'); continue;}
int isA = 1;
for (int j = 0; j <= M; j++) if (dp1[i - 1][j] + dp2[i + 1][M - j] == ans) {isA = 0; break;}
if (isA) putchar('A');
else putchar('B');
}
return 0;
}