不想说啥了,比赛期间智商全程下线
A
设$f[i][j]$表示前$i$个位置,前缀和为$j$的方案数,转移的时候该位置放了什么,以及该位置之前的和是多少。
发现第二维可以前缀和优化。
不用管代码里的fib是什么,当时傻了在xjb分析下界。。。。
时间复杂度:$O(nk)$
/* */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> #include<set> #include<queue> #include<cmath> //#include<ext/pb_ds/assoc_container.hpp> //#include<ext/pb_ds/hash_policy.hpp> #define Pair pair<int, int> #define MP(x, y) make_pair(x, y) #define fi first #define se second #define int long long #define LL long long #define ull unsigned long long #define rg register #define pt(x) printf("%d ", x); //#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++) //char buf[(1 << 22)], *p1 = buf, *p2 = buf; //char obuf[1<<24], *O = obuf; //void print(int x) {if(x > 9) print(x / 10); *O++ = x % 10 + '0';} //#define OS *O++ = ' '; using namespace std; //using namespace __gnu_pbds; const int MAXN = 1e6 + 10, INF = 1e9 + 10, mod = 998244353; const double eps = 1e-9; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int f[21][MAXN], s[MAXN], fib[MAXN]; main() { int N = read(), K = read();//k个位置 总和为n fib[1] = 1; fib[2] = 1; for(int i = 3; i <= N; i++) fib[i] = fib[i - 1] + fib[i - 2]; for(int i = 1; i <= N; i++) f[1][i] = 1, s[i] = (s[i - 1] + f[1][i]) % mod; for(int i = 2; i <= K; i++) { for(int j = 1; j <= N; j++) { // for(int k = fib[i - 1]; k <= j / 2; k++) (f[i][j] += f[i - 1][k]) %= mod; f[i][j] = s[j / 2] % mod; if((j / 2 >= fib[i - 1]) && (fib[i - 1] >= 1)) f[i][j] = (f[i][j] - s[fib[i - 1] - 1] + mod) % mod; } s[0] = 0; for(int j = 1; j <= N; j++) s[j] = (s[j - 1] + f[i][j]) % mod; } printf("%d", f[K][N] % mod); return 0; } /* 15 3 */
B
起点和终点都是已知的,那么对于其他的节点,一定是从根节点走到该节点再往回走。
这样我们记录下根节点到其他节点路径的并,在这之中唯一不需要经过两次的是该节点到根的路径,减去即可
由于每个点只会经过一次,因此时间复杂度为:$O(n+m)$
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int N, M, dep[MAXN], fa[MAXN], vis[MAXN]; vector<int> v[MAXN]; void dfs(int x, int _fa) { dep[x] = dep[_fa] + 1; fa[x] = _fa; for(int i = 0; i < v[x].size(); i++) { int to = v[x][i]; if(to == _fa) continue; dfs(to, x); } } int main() { N = read(); M = read(); for(int i = 1; i <= N - 1; i++) { int x = read(), y = read(); v[x].push_back(y); v[y].push_back(x); } dep[0] = -1; dfs(1, 0); vis[1] = 1; int tot = 0, ans = 0; for(int i = 1; i <= M; i++) { int x = read(), tmp = x; while(!vis[x]) tot++, vis[x] = 1, x = fa[x]; printf("%d ", 2 * tot - dep[tmp]); } return 0; } /* */
C
神仙题Orz
题目里有个很良心的部分分
这就提示我们跟二进制拆分有点关系了
序列上的问题好像很难搞,我们扔到环上去(这是怎么想到的啊Orz)
构造一个这样的环
上下两个$0$不管进化几次肯定都是$0$(左右对称)
考虑其他位置,观察每一项展开后的式子不难发现(发现不了。。)
对于位置$i$,如果$a[i]$进化了$2^d$后变为$c[i]$,那么$c[i] = a[i - 2^d] + a[i+2^d]$
做完了。。
#include<cstdio> #include<cstring> #define int long long using namespace std; const int MAXN = 1e6 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int T, N; char s[MAXN]; int a[MAXN], b[MAXN]; main() { T = read(); N = read(); // for(int i = 1; i <= N; i++) a[i] = read(); scanf("%s", s + 1); for(int i = 1; i <= N; i++) a[i] = s[i] - '0', a[N * 2 + 2 - i] = a[i]; N = N * 2 + 2; for(int j = 61; j >= 0; j--) { if(T & (1ll << j)) { int base = (1ll << j) % N; for(int i = 0; i <= N; i++) b[i] = a[(i - base + N) % N] ^ a[(i + base) % N]; memcpy(a, b, sizeof(a)); } } for(int i = 1; i <= N / 2 - 1; i++) printf("%d", a[i]); return 0; } /* 2 5 10010 */