来自学长的馈赠2
A. 随
我到现在还理解的不太深刻……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const ll mod = 1e9 + 7; const int INF = 0x7ffffff; #define int long long int n, m, p, g[1005], f[1005], fz[1005]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int qpow(int a, int b) { int ans = 1; while(b) { if(b&1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } void hehe() { for(int i=1; i<p; i++) fz[i] = 0; for(int i=1; i<p; i++) { for(int j=1; j<p; j++) { fz[i*j%p] = (fz[i*j%p]+f[i]*f[j]%mod)%mod; } } for(int i=1; i<p; i++) { f[i] = fz[i]; } } signed main() { scanf("%lld%lld%lld", &n, &m, &p); for(int i=1; i<=n; i++) { int x = read(); f[x]++; } g[1] = 1; int ha = m; for(; ha; ha>>=1, hehe()) { if(ha&1) { for(int i=1; i<p; i++) fz[i] = 0; for(int i=1; i<p; i++) { for(int j=1; j<p; j++) { fz[i*j%p] = (fz[i*j%p]+g[i]*f[j]%mod)%mod; } } for(int i=1; i<p; i++) { g[i] = fz[i]; } } } int ans = 0; for(int i=1; i<p; i++) { ans = (ans+i*g[i]%mod)%mod; } printf("%lld", ans*qpow(qpow(n, m), mod-2)%mod); return 0; }
B. 单
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const ll mod = 998244353; const int INF = 0x7ffffff; int T, n, t, f[maxn][31], cost[maxn][31], dep[maxn]; int ans[maxn], c[maxn], size[maxn]; double mp[1001][1001]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int next, to, w; }a[maxn<<1]; int head[maxn], len; inline void add(int x, int y, int w) { a[++len].to = y; a[len].next = head[x]; a[len].w = w; head[x] = len; } void dfs(int u, int fa) { f[u][0] = fa; dep[u] = dep[f[u][0]]+1; for(int i=1; i<31; i++) { f[u][i] = f[f[u][i-1]][i-1]; cost[u][i] = cost[f[u][i-1]][i-1]+cost[u][i-1]; } for(int i=head[u]; i; i=a[i].next) { int v = a[i].to, w = a[i].w; if(v == fa) continue; cost[v][0] = w; dfs(v, u); } } int lca(int x, int y) { if(dep[x] > dep[y]) swap(x, y); int tmp = dep[y] - dep[x], ans = 0; for(int j=0; tmp; j++,tmp>>=1) { if(tmp & 1) ans += cost[y][j], y = f[y][j]; } if(y == x) return ans; for(int j=30; j>=0 && x!=y; j--) { if(f[x][j] != f[y][j]) { ans += cost[x][j]+cost[y][j]; x = f[x][j]; y = f[y][j]; } } ans += cost[x][0]+cost[y][0]; return ans; } void CatAKIOI() { int nwline = 0; for(int k=0; k<n; k++) { int maxi = nwline; for(int i=nwline+1; i<n; i++) { if(abs(mp[i][k]) > abs(mp[maxi][k])) maxi = i; } if(mp[maxi][k] == 0) continue; for(int j=0; j<n+1; j++) { swap(mp[nwline][j], mp[maxi][j]); } for(int i=0; i<n; i++) { if(i == nwline) continue; double mul = mp[i][k] / mp[nwline][k]; for(int j=k+1; j<n+1; j++) { mp[i][j] -= mp[nwline][j] * mul; } } nwline++; } for(int i=0; i<n; i++) { if(int(mp[i][n] / mp[i][i] * 100) == 0) { printf("0 "); } else printf("%.0lf ", mp[i][n] / mp[i][i]); } printf("\n"); } void dfs2(int x, int fa) { size[x] = c[x]; for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; dfs2(v, x); size[x] += size[v]; } } void dfs3(int x, int fa) { if(x != 1) { ans[x] = ans[fa] + (size[1]-size[x]) - size[x]; //printf("%d %d %d\n", ans[fa], size[1], size[x]); } for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; dfs3(v, x); } } void solve1() { memset(ans, 0, sizeof(ans)); for(int i=1; i<=n; i++) { c[i] = read(); } for(int i=2; i<=n; i++) { ans[1] += c[i] * lca(1, i); } dfs2(1, 0); //for(int i=1; i<=n; i++) printf("%d\n", size[i]); dfs3(1, 0); for(int i=1; i<=n; i++) { printf("%d ", ans[i]); } printf("\n"); } void solve2() { memset(mp, 0, sizeof(mp)); memset(ans, 0, sizeof(ans)); for(int i=0; i<n; i++) { mp[i][n] = read(); } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { mp[i-1][j-1] = lca(i, j); } } CatAKIOI(); } int main() { T = read(); while(T--) { memset(head, 0, sizeof(head)); memset(f, 0, sizeof(f)); memset(dep, 0, sizeof(dep)); memset(cost, 0, sizeof(cost)); n = read(); for(int i=1; i<n; i++) { int x = read(), y = read(); add(x, y, 1); add(y, x, 1); } dfs(1, 0); t = read(); if(t == 0) solve1(); else solve2(); /*for(int i=1; i<=5; i++) { int x = read(), y = read(); printf("%d\n", lca(x, y)); }*/ } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const ll mod = 998244353; const int INF = 0x7ffffff; int T, n, c[maxn], b[maxn], dis[maxn], t; ll size[maxn], sum; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int next, to; }a[maxn<<1]; int head[maxn], len; inline void add(int x, int y) { a[++len].to = y; a[len].next = head[x]; head[x] = len; } void dfs1(int x, int fa) { size[x] = c[x]; dis[x] = dis[fa] + 1; for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; dfs1(v, x); size[x] += size[v]; } } void getb(int x, int fa) { for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; b[v] = b[x] + (size[1]-size[v])-size[v]; //printf("b[%d] = %d\n", v, b[v]); getb(v, x); } } void dfs2(int x, int fa) { for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; sum += b[v] - b[x]; //printf("sum += b[%d] - b[%d]\n", v, x); dfs2(v, x); } } void geta(int x, int fa) { int tmp = 0; for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; size[v] = (b[x] - b[v] + sum) >> 1; tmp += size[v]; geta(v, x); } c[x] = size[x] - tmp; } int main() { dis[0] = -1; T = read(); while(T--) { sum = 0; n = read(); for(int i=1; i<n; i++) { int x = read(), y = read(); add(x, y); add(y, x); } t = read(); if(t == 0) { for(int i=1; i<=n; i++) { c[i] = read(); } dfs1(1, 0); b[1] = 0; for(int i=1; i<=n; i++) { b[1] += c[i] * dis[i]; } getb(1, 0); for(int i=1; i<=n; i++) { printf("%d ", b[i]); } printf("\n"); } else { for(int i=1; i<=n; i++) { b[i] = read(); } dfs2(1, 0); sum = (sum + 2 * b[1]) / (n - 1); size[1] = sum; geta(1, 0); for(int i=1; i<=n; i++) { printf("%d ", c[i]); } printf("\n"); } for(int i=1; i<=n; i++) { head[i] = 0; } len = 0; } return 0; }
我连a->b的推导式子都没想出来,于是就先暴力了一下,用lca带长度的那种倍增求出树上任意两点间的距离,如果t=0就根据定义直接循环相加,如果t=1就把求出来的这些距离当做系数,高斯消元。
正解是添加一个size[]数组记录以i为根的子树的点权和,这样就可以换根了,除了1(或以任意一个为根)的b数组可以从他的父节点转移,1的b值可以用深度来计算。
f[v] = f[x] + (size[1]-size[v]) - size[v] 考虑换根后哪些变多了,哪些变少了。
然后移项可得:f[v] - f[x] = size[1] - 2 * size[v]
如果把遍历整棵树的f[v]-f[x]相加,就可以得到n-1个size[1]和-2倍的size[2]到size[n]的和,另一个重要的式子可以用于消掉这些size[2]到size[n]:b[1] = size[2] + …… + size[n],所以加上两个b[1]就可以求出size[1],其他的size也就可以知道了。
C. 题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 5; const ll mod = 1e9 + 7; const int INF = 0x7ffffff; int n, typ, mub[maxn], inv[maxn], f[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int qpow(int a, int b) { int ans = 1; while(b) { if(b & 1) ans = ans*1ll*a%mod; a = a*1ll*a%mod; b >>= 1; } return ans; } int C(int n, int m) { if(n < m) return 0; return mub[n]*1ll*inv[m]%mod*inv[n-m]%mod; } void init() { mub[0] = 1; for(int i=1; i<maxn; i++) mub[i] = mub[i-1]*1ll*i%mod; //for(int i=1; i<maxn; i++) printf("mub[%d] = %d\n", i, mub[i]); inv[maxn-1] = qpow(mub[maxn-1], mod-2); //printf("%d\n", inv[maxn-1]); for(int i=maxn-1; i>=1; i--) inv[i-1] = inv[i]*1ll*i%mod; } int catalan(int n) { return C(n*2, n)*1ll*qpow(n+1, mod-2)%mod; } int main() { init(); n = read(); typ = read(); if(typ == 0) { int ans = 0; for(int i=0; i<=n; i+=2) { //printf("%d %d %d\n", C(i, i/2), C(n, i), C((n-i), (n-i)/2)); ans = (ans+C(n, i)*1ll*C(i, i/2)%mod*C((n-i), (n-i)/2)%mod)%mod; } printf("%d", ans); } else if(typ == 1) { printf("%d", catalan(n/2)); } else if(typ == 2) { n >>= 1; f[0] = 1; f[1] = 4; for(int i=2; i<=n; i++) { for(int j=1; j<=i; j++) { f[i] = (f[i]+f[i-j]*4ll*catalan(j-1)%mod)%mod; } } printf("%d", f[n]); } else { int ans = 0; for(int i=0; i<=n; i+=2) { ans = (ans+C(n, i)*1ll*catalan(i/2)%mod*catalan((n-i)/2))%mod; } printf("%d", ans); } return 0; }
嘲笑一下自己,本来想不看题解里的代码用自己的板子,抄了半天都不对,然后发现我把C函数的两个参数传反了,怪不得得数全是0。
对于typ=1的数据:答案为catalan数,使用O(n)的catalan数递推公式或者利用组合数O(1)计算均可.catalan(n)=C(2n,n)/(n+1)
对于typ=0的数据:枚举横向移动了多少步.横向移动i步时(为了存在合法解,i必须是偶数),方案数为C(n,i)*C(i,i/2)*C((n-i),(n-i)/2),先枚举多少横向的,再枚举在这些里面哪些向左(由于要回到原点所以/2,再枚举那些向上)。
对于typ=2的数据:f[i]表示走了i步回到原点的方案数,枚举第一次回到原点时走过的步数j(为了存在合法解,j为偶数),则此时方案数为f[i-j]*catalan(j/2-1)*4,复杂度为O(n^2)
因为卡特兰数要求只能单向移动,所以要找第一次经过原点,四个方向都一样所以只考虑一个,在这个方向上不经过原点相当于先向远离坐标轴的方向走了一个单位长度,这一个单位长度的路径只会作为整条路径的开头或结尾出现,没有选择还是一个方案。
对于typ=3的数据:枚举横向移动了多少步.横向移动i步时(为了存在合法解,i必须是偶数),方案数为C(n,i)*catalan(i/2)*catalan((n-i)/2)
D. DP搬运工1
感觉有一个题和它有一点相似——考试:高一提高组过渡-中国象棋
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ll; const int maxn = 103; const int inf = 1e9 + 7; const int mod = 9999973; //把mod和inf写反也真是没谁了 int n, m; ll f[maxn][maxn][maxn], ans; /* f[i][j][k]代表放了前i行,有j列是有一个棋子,有k列是有两个棋子的合法方案数 总列数m-j-k=空的列数 1.在第i行不放棋子 f[i][j][k] = f[i-1][j][k] 2.放一个 ->放在已经有一个棋子的列 由f[i-1][j+1][k-1]转移 ->放在当前还没有棋子的列 由f[i-1][j-1][k]转移 3.放两个 ->一个放在有一个棋子的列,另一个放在没有棋子的列 …… */ inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } inline int C(int a) { return (a*(a-1)/2)%mod; } int main() { freopen("chess.in", "r", stdin); freopen("chess.out", "w", stdout); n = read(); m = read(); f[0][0][0] = 1; for(int i=1; i<=n; i++) { for(int j=0; j<=m; j++) { for(int k=0; k<=m-j; k++) { f[i][j][k] = f[i-1][j][k]; //判断条件只是为了让下标不要为负 if(k >= 1) f[i][j][k] += f[i-1][j+1][k-1] * (j+1); if(j >= 1) f[i][j][k] += f[i-1][j-1][k] * (m-j-k+1); if(j >= 2) f[i][j][k] += f[i-1][j-2][k] * C(m-j-k+2); if(k >= 1) f[i][j][k] += f[i-1][j][k-1] * j * (m-j-k+1); if(k >= 2) f[i][j][k] += f[i-1][j+2][k-2] * C(j+2); f[i][j][k] %= mod; } } } for(int i=0; i<=m; i++) { //原来那个错了是因为写成了m-j,,,还自以为高明的得出了结论。。。 //忒可笑了吧 for(int j=0; j<=m-i; j++) { ans = (ans + f[n][i][j]) % mod; } } printf("%lld", ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 55; const ll mod = 998244353; const int INF = 0x7ffffff; int n, m; ll f[2][maxn][maxn*maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int main() { n = read(); m = read(); f[1][0][0] = 1; int now = 1; for(int i=2; i<=n; i++) { now ^= 1; for(int j=0; j<maxn; j++) { for(int k=0; k<maxn*maxn; k++) { f[now][j][k] = 0; } } int maxj = min(i, n-i)+1, maxk = min(m, i*i); /*for(int j=0; j<=maxj; j++) { for(int k=0; k<=maxk; k++) { if(f[now^1][j][k] == 0) continue; f[now][j+1][k] = (f[now][j+1][k]+f[now^1][j][k]*2ll)%mod; f[now][j][k+i] = (f[now][j][k+i]+f[now^1][j][k]*2ll)%mod; if(j == 0) continue; f[now][j+1][k] = (f[now][j+1][k]+f[now^1][j][k]*j)%mod; f[now][j][k+i] = (f[now][j][k+i]+f[now^1][j][k]*j*2ll)%mod; f[now][j-1][k+i+i] = (f[now][j-1][k+i+i]+f[now^1][j][k]*j)%mod; } }*/ for(int j=0; j<=maxj+2; j++) { for(int k=0; k<=maxk+2; k++) { if(j>=1) f[now][j][k] = (f[now][j][k]+f[now^1][j-1][k]*2)%mod; if(k>=i) f[now][j][k] = (f[now][j][k]+f[now^1][j][k-i]*2)%mod; if(j>0) f[now][j][k] = (f[now][j][k]+f[now^1][j-1][k]*(j-1))%mod; if(j>0 && k>=i) f[now][j][k] = (f[now][j][k]+f[now^1][j][k-i]*j*2)%mod; f[now][j][k] = (f[now][j][k]+f[now^1][j+1][k-i-i]*(j+1))%mod; } } } ll ans = 0; for(int i=0; i<=m; i++) { ans = (ans+f[now][0][i])%mod; } printf("%lld\n", ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 55; const ll mod = 998244353; const int INF = 0x7ffffff; int n, m; ll f[2][maxn][maxn*maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int main() { n = read(); m = read(); f[1][0][0] = 1; int now = 1; for(int i=2; i<=n; i++) { now ^= 1; for(int j=0; j<maxn; j++) { for(int k=0; k<maxn*maxn; k++) { f[now][j][k] = 0; } } int maxj = min(i, n-i)+1, maxk = min(m, i*i); for(int j=0; j<=maxj; j++) { for(int k=0; k<=maxk; k++) { if(f[now^1][j][k] == 0) continue; f[now][j+1][k] = (f[now][j+1][k]+f[now^1][j][k]*2ll)%mod; f[now][j][k+i] = (f[now][j][k+i]+f[now^1][j][k]*2ll)%mod; if(j == 0) continue; f[now][j+1][k] = (f[now][j+1][k]+f[now^1][j][k]*j)%mod; f[now][j][k+i] = (f[now][j][k+i]+f[now^1][j][k]*j*2ll)%mod; f[now][j-1][k+i+i] = (f[now][j-1][k+i+i]+f[now^1][j][k]*j)%mod; } } } ll ans = 0; for(int i=0; i<=m; i++) { ans = (ans+f[now][0][i])%mod; } printf("%lld\n", ans); return 0; }
奇怪的dp名称增加了——预设型dp
按照从1到n的顺序开始填数,在这些数中间会出现一些空位,f[i][j][k]的含义是填完1到i,在已填的数中间有j个空位,贡献总和为k。分几种情况:
1.在两边填数,因为新填的数一定大于已填的数,仍然为空位的数一定要大于这个新填的数,如果这个数要有贡献,只能是【已填】【空位若干】【已填】【New】,这时贡献为i。如果没有贡献,新填的数就要远离一位,使中间未来会有一个更大的数填入,空位就多了一个。如果有一个贡献,那就需要紧贴,空位数不变。
这两种情况都因为分左右两边让方案数加上了填入新数之前的方案数的二倍。