有点自闭,本来应该昨天写的,拖到了今天
1001. Blank
upsolved
题意是在\(n\)个位置上填数,只能填\(0,1,2,3\)这四种,然后有\(m\)个限制条件,限制的是区间不同数的个数,求填数方案数\(1<=n,m<=100\) 看着官方题解一下就明白了
\(dp[i][j][k][t]\)代表填完前\(t\)个数之后四种数的出现位置从小大大排序分别是\(i,j,k,t\)的方案数,限制条件加到右端点里判断就好了,\(t\)那一维可以滚动掉,时间复杂度\(O(Tn^4)\)(因为是\(i,j,k,t\)有序的所以常数比一小得多),空间复杂度\(O(n^3)\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <bits/stdc++.h> using namespace std;
const int N = 101, mod = 998244353;
int dp[N][N][N][2], T, n, m, l, r, x, ans;
vector<pair<int, int>> cons[N];
void addmod(int &x, int y) { x += y; if(x >= mod) x -= mod; }
int main() { scanf("%d", &T); while(T--) { ans = 0; scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i) { scanf("%d%d%d", &l, &r, &x); cons[r].push_back({l, x}); } for(int i = 1; i <= n; ++i) cons[i].push_back({i, 1}); memset(dp, 0, sizeof(dp)); dp[0][0][0][0] = 1; for(int cur = 1; cur <= n; ++cur) { int o = cur & 1; for(int i = 0; i <= cur; ++i) for(int j = i; j <= cur; ++j) for(int k = j; k <= cur; ++k) dp[i][j][k][o] = 0; for(int i = 0; i <= cur; ++i) for(int j = i; j <= cur; ++j) for(int k = j; k <= cur; ++k) { addmod(dp[j][k][cur - 1][o], dp[i][j][k][o ^ 1]); addmod(dp[i][k][cur - 1][o], dp[i][j][k][o ^ 1]); addmod(dp[i][j][cur - 1][o], dp[i][j][k][o ^ 1]); addmod(dp[i][j][k][o], dp[i][j][k][o ^ 1]); } for(int i = 0; i <= cur; ++i) { for(int j = i; j <= cur; ++j) for(int k = j; k <= cur; ++k) for(auto c : cons[cur]) { l = c.first, x = c.second; if((i >= l) + (j >= l) + (k >= l) + (cur >= l) != x) dp[i][j][k][o] = 0; } } } for(int i = 0; i <= n; ++i) for(int j = i; j <= n; ++j) for(int k = j; k <= n; ++k) addmod(ans, dp[i][j][k][n & 1]); printf("%d\n", ans); for(int i = 1; i <= n; ++i) cons[i].clear(); } return 0; }
|
1002. Operation
upsloved
强制在线,每次往数列末加数或询问区间子集异或最大值
究极自闭,cf1100F原题(有在线做法),这题我寒假的时候还做过。。。然后看了五个小时没看出来。。。
就是记录\(n\)个前缀线性基,线性基额外维护当前位置的数的插入位置,插入的时候贪心地取最大值,具体可以搜cf1100F的题解
1004. Vacation
solved at 02:40
有\(n+1\)辆车,每辆车有长度最大速度以及离终点的距离,不能超车,可以贴着前面车走,问离终点最远的车到达终点的时间
队友想出了二分答案的做法
有线性做法:离终点最远的车肯定最后是和其他若干辆车(可能只有它自己)连在一起过终点的,\(O(n)\)枚举取最大值就好了
1005. Path
solved at 02:06
有一个有向图,拆除一条边的代价是这条边的长度,要求使用最小的代价使得从\(1\)到\(n\)的最短路变长
把最短路上的边拿出来建一张新图跑最小割就行了
队友建新图的时候点没标记疯狂\({\rm MLE}\)...
1006. Typewriter
upsolved
用最小的代价构建目标字符串,你一开始有一个空串,有两种操作
\(1\):花费\(p\)的代价往你的串最后加一个字符
\(2\):花费\(q\)的代价往你的串最后加一个你的串的任意子串
比赛上来读了个假题。。。
维护两个位置\(i, j\)
\(s[1:j-1]\)插入\({\rm SAM}\),然后看\(s[j:i]\)是不是\(s[1:j-1]\)的子串
不是就插入\(s[j], j++\)
是就\(dp[i] = min(dp[i - 1] + p, dp[j-1]+q])\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include <bits/stdc++.h> using namespace std;
const int N = 2e5 + 10;
int nxt[N << 1][26], len[N << 1], par[N << 1]; int sz, last; char s[N]; int n, p, q; long long dp[N];
int newnode(int l) { len[sz] = l; memset(nxt[sz], 0, sizeof(nxt[sz])); return sz++; }
void init() { sz = last = 0; par[sz] = -1; newnode(0); }
void add(int x) { int p = last, np = newnode(len[last] + 1); for(; ~p && !nxt[p][x]; p = par[p]) nxt[p][x] = np; if(p == -1) { par[np] = 0; } else { int q = nxt[p][x]; if(len[q] == len[p] + 1) { par[np] = q; } else { int nq = newnode(len[p] + 1); memcpy(nxt[nq], nxt[q], sizeof(nxt[nq])); par[nq] = par[q]; par[q] = par[np] = nq; for(; ~p && nxt[p][x] == q; p = par[p]) nxt[p][x] = nq; } } last = np; }
int main() { while(~scanf("%s%d%d", s + 1, &p, &q)) { init(); n = strlen(s + 1); int cur = 1, now = 1, pos = 0; while(cur <= n) { int x = s[cur] - 'a'; if(nxt[pos][x]) { pos = nxt[pos][x]; dp[cur] = min(dp[cur - 1] + p, dp[now - 1] + q); cur++; } else if(cur == now) { add(x); dp[cur] = dp[cur - 1] + p; cur++; now++; } else { add(s[now++] - 'a'); int tlen = cur - now; while(~pos && ~par[pos] && len[par[pos]] >= tlen) pos = par[pos]; } } printf("%lld\n", dp[n]); } return 0; }
|
1009. String
upsolved
赛中队友说可做,只是我一直在1002...我真菜
选择一个给定字符串的一个长度为\(k\)的子序列构成目标串,要求字典序最小,同时给出了\(26\)种字母在新串中出现次数的上界和下界
直接贪心就完事了。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| #include <bits/stdc++.h> using namespace std;
const int N = 1e5 + 10, knd = 26;
char s[N]; int L[knd], R[knd], cnt[N][knd], c[knd], n, k, nxt[N][knd], pos, tmp, tot; char ans[N];
bool check(int p, int now) { if(p < 0) return false; int x = s[p] - 'a', res = 0, ret = 1; if(c[x] == R[x]) return false; c[x]++; for(int i = 0; i < knd; ++i) { if(L[i] > c[i]) res += L[i] - c[i]; if(cnt[p][i] - (x == i) < L[i] - c[i]) { ret = 0; break; } } if(res > k - now) ret = 0; c[x]--; return ret; }
int main() { while(~scanf("%s%d", s, &k)) { tot = 0; n = strlen(s); for(int i = 0; i < knd; ++i) { scanf("%d%d", &L[i], &R[i]); tot += R[i]; } memset(nxt[n], -1, sizeof(nxt[n])); memset(cnt[n], 0, sizeof(cnt[n])); memset(c, 0, sizeof(c)); for(int i = n - 1; ~i; --i) { memcpy(nxt[i], nxt[i + 1], sizeof(nxt[i + 1])); memcpy(cnt[i], cnt[i + 1], sizeof(cnt[i + 1])); nxt[i][s[i] - 'a'] = i; cnt[i][s[i] - 'a']++; } pos = -1; for(int i = 0; i < knd; ++i) { if(check(nxt[0][i], 1)) { c[i]++; pos = nxt[0][i] + 1; ans[0] = 'a' + i; break; } } if(pos == -1 || tot < k) { puts("-1"); continue; } for(int i = 1; i < k; ++i) { for(int j = 0; j < knd; ++j) { if(c[j] == R[j]) continue; if(check(nxt[pos][j], i + 1)) { c[j]++; pos = nxt[pos][j] + 1; ans[i] = 'a' + j; break; } } } ans[k] = '\0'; printf("%s\n", ans); } return 0; }
|
1012. Sequence
NTT
参考博客
1011,1013都是队友负责的方向。。
这场打的真是菜,全场没什么贡献,以后学了什么新东西还是应该记录下来啊。。。