文章目录
- A Social Distancing
- B Mask Allocation
- C A National Pandemic
- D Fake News
- G Topo Counting
- H Dividing
- I Valuable Forests
- J Pointer Analysis
A Social Distancing
在半径为 r r r的圆内的格点中放入 n n n个点.
使得两两距离的平方之和最大.
n ≤ 8 , r ≤ 30 n\le 8,r\le 30 n≤8,r≤30.
一开始想到搜索但是点数过多了.
我们来考虑推式子优化:
∑ i = 1 n ∑ j = i + 1 n d ( i , j ) = ∑ i = 1 n ∑ j = i + 1 n x i 2 + y i 2 + x j 2 + y j 2 − 2 x i x j − 2 y i y j = n ( ∑ i = 1 n x i 2 + y i 2 ) − ( ∑ x i ) 2 − ( ∑ y i ) 2 \sum_{i=1}^n \sum_{j=i+1}^n d(i,j)=\sum_{i=1}^n \sum_{j=i+1}^n x_i^2+y_i^2+x_j^2+y_j^2-2x_ix_j-2y_iy_j=n(\sum_{i=1}^n x_i^2+y_i^2)-(\sum x_i)^2-(\sum y_i)^2 ∑i=1n∑j=i+1nd(i,j)=∑i=1n∑j=i+1nxi2+yi2+xj2+yj2−2xixj−2yiyj=n(∑i=1nxi2+yi2)−(∑xi)2−(∑yi)2
考虑dp, f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示选 i i i个点, ∑ x = j \sum x=j ∑x=j, ∑ y = k \sum y=k ∑y=k.
然后 a n s [ i ] [ r ] = max { n ∗ f [ i ] [ j ] [ k ] − j 2 − k 2 } ans[i][r]=\max\{n*f[i][j][k]-j^2 -k^2\} ans[i][r]=max{n∗f[i][j][k]−j2−k2}
这样做肯定会T的–打表就好.
打表程序:
#include<bits/stdc++.h>
#define x first
#define y second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=2e5+10,size=1<<20,mod=998244353,inf=2e9;//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {char c=gc; x=0; int f=1;while(!isdigit(c)){if(c=='-')f=-1; c=gc;}while(isdigit(c)) x=x*10+c-'0',c=gc;x*=f;
}
template<class o> void qw(o x) {if(x/10) qw(x/10);putchar(x%10+'0');
}
template<class o> void pr1(o x) {if(x<0)x=-x,putchar('-');qw(x); putchar(' ');
}
template<class o> void pr2(o x) {if(x<0)x=-x,putchar('-');qw(x); putchar('\n');
}int T,n,r,f[10][610][610],ans[11][33];
vector<pii> s;
const int base=303;void upd(int &x,int y) {x=max(x,y);}int main() {r=30;for(int i=-r;i<=r;i++)for(int j=-r;j<=r;j++)if(i*i+j*j<=r*r) s.pb(mk(i,j));memset(f,-2,sizeof f);f[0][base][base]=0;puts("{");for(r=1;r<=30;r++) {for(auto it:s) if(it.x*it.x+it.y*it.y<=r*r)for(int i=0;i<8;i++)for(int j=base-r*i;j<=base+r*i;j++)for(int k=base-r*i;k<=base+r*i;k++) if(f[i][j][k]>=0) upd(f[i+1][j+it.x][k+it.y],f[i][j][k]+it.x*it.x+it.y*it.y);for(int i=1;i<=8;i++)for(int j=base-r*i;j<=base+r*i;j++)for(int k=base-r*i;k<=base+r*i;k++) if(f[i][j][k]>=0) ans[i][r]=max(ans[i][r],(i*f[i][j][k]-(j-base)*(j-base)-(k-base)*(k-base)));printf("\t{");for(int i=1;i<=8;i++) printf("%d,",ans[i][r]);puts("},");}puts("};");
}
AC代码:
#include<bits/stdc++.h>
using namespace std;void qr(int &x) {scanf("%d",&x);}int T,n,r;int ans[][11]= {{0},{0,4,8,16,24,36,48,64,},{0,16,32,64,96,144,192,256,},{0,36,76,144,218,324,432,576,},{0,64,130,256,384,576,768,1024,},{0,100,224,400,624,900,1224,1600,},{0,144,312,576,880,1296,1740,2304,},{0,196,416,784,1188,1764,2356,3136,},{0,256,554,1024,1572,2304,3102,4096,},{0,324,722,1296,2014,2916,3954,5184,},{0,400,896,1600,2496,3600,4896,6400,},{0,484,1064,1936,2984,4356,5872,7744,},{0,576,1248,2304,3520,5184,6960,9216,},{0,676,1512,2704,4224,6084,8280,10816,},{0,784,1746,3136,4870,7056,9564,12544,},{0,900,2016,3600,5616,8100,11016,14400,},{0,1024,2264,4096,6336,9216,12456,16384,},{0,1156,2600,4624,7224,10404,14160,18496,},{0,1296,2888,5184,8056,11664,15816,20736,},{0,1444,3218,5776,9008,12996,17666,23104,},{0,1600,3584,6400,9984,14400,19584,25600,},{0,1764,3912,7056,10942,15876,21500,28224,},{0,1936,4344,7744,12080,17424,23688,30976,},{0,2116,4712,8464,13144,19044,25808,33856,},{0,2304,5138,9216,14326,20736,28122,36864,},{0,2500,5612,10000,15624,22500,30624,40000,},{0,2704,6062,10816,16896,24336,33120,43264,},{0,2916,6536,11664,18184,26244,35664,46656,},{0,3136,6984,12544,19488,28224,38266,50176,},{0,3364,7520,13456,20968,30276,41200,53824,},{0,3600,8084,14400,22480,32400,44076,57600,},
};int main() {qr(T); while(T--) qr(n),qr(r),printf("%d\n",ans[r][n-1]);
}
B Mask Allocation
n ∗ m n*m n∗m 个口罩,分成若干个单元,同时满足 能分成和为 n n n的 m m m组 还有 和为 m m m的 n n n组.
求最小化单元数的情况下字典序最大的方案.
n , m ≤ 1 e 4 n,m\le 1e4 n,m≤1e4.
设求解函数为 f ( n , m ) f(n,m) f(n,m).
令 n ≤ m n\le m n≤m.
那么要最小化单元数 就必须每个单元都尽量的大.
明显每个单元 ≤ n \le n ≤n.
那么对于第二种情况, n n n组,每组 m m m个.
m m m个用 n n n来覆盖,那么剩余 n n n组,每组 m m o d n m\mod n mmodn.
对于第一种情况,剩余 m m o d n m\mod n mmodn组,每组 n n n个.
发现 组 和 总和 是互换关系,即 f ( m m o d n , n ) f(m\mod n,n) f(mmodn,n).
复杂度为 O ( log n + log m ) O(\log n+\log m) O(logn+logm)(不计输出)
int T,n,m;int main() {qr(T); while(T--) {qr(n); qr(m);vector<int> ans;while(1) {if(n>m) swap(n,m);if(!n) break;int i=m/n*n;while(i--) ans.pb(n);m %= n;}pr2(SZ(ans));for(int i:ans) pr1(i);puts("");}
}
C A National Pandemic
初始 ∀ i , f ( i ) = 0 \forall i,f(i)=0 ∀i,f(i)=0.
你需要执行3类操作:
- 有两个参数 x , w x,w x,w, ∀ y , f ( y ) + = w − d i s ( x , y ) \forall y,f(y) += w-dis(x,y) ∀y,f(y)+=w−dis(x,y).
- 参数为 x x x, f ( x ) = min ( f ( x ) , 0 ) f(x)=\min(f(x),0) f(x)=min(f(x),0).
- 参数为 x x x,询问 f ( x ) f(x) f(x).
主要难处理的是第一项:
我们的突破口是 d i s ( x , y ) dis(x,y) dis(x,y).
Δ = w − d i s ( x , y ) = w − ( d e p [ x ] + d e p [ y ] − 2 d e p [ l c a ( x , y ) ] ) \Delta =w-dis(x,y)=w-(dep[x]+dep[y]-2dep[lca(x,y)]) Δ=w−dis(x,y)=w−(dep[x]+dep[y]−2dep[lca(x,y)])
我们可以全局记录一个 Δ = w − d e p [ x ] \Delta=w-dep[x] Δ=w−dep[x].
然后我们把 x x x到根的路径每个点都+2.(表示+2dep[lca(x,y)]).
我们记录一下前面的操作一次数 t t t,那么询问的时候加上 t ∗ d e p [ y ] t *dep[y] t∗dep[y],
然后询问 y y y到根的路径和.(两条路径的公共部分就是 d e p [ l c a ] dep[lca] dep[lca])
总复杂度为 O ( m log 2 n ) O(m\log^2 n) O(mlog2n).
听说可以用点分树,但是不会.
#include<bits/stdc++.h>
#define x first
#define y second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=5e4+10,size=1<<20,mod=998244353,inf=2e9;//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {char c=gc; x=0; int f=1;while(!isdigit(c)){if(c=='-')f=-1; c=gc;}while(isdigit(c)) x=x*10+c-'0',c=gc;x*=f;
}
template<class o> void qw(o x) {if(x/10) qw(x/10);putchar(x%10+'0');
}
template<class o> void pr1(o x) {if(x<0)x=-x,putchar('-');qw(x); putchar(' ');
}
template<class o> void pr2(o x) {if(x<0)x=-x,putchar('-');qw(x); putchar('\n');
}int T,n,m,fa[N],dep[N],son[N],sz[N];
struct edge{int y,next; } a[N*2]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len; }void dfs(int x) {sz[x]=1; son[x]=0;for(int k=last[x],y;k;k=a[k].next) if((y=a[k].y)^fa[x]) {fa[y]=x;dep[y]=dep[x]+1;dfs(y);sz[x] += sz[y];if(sz[son[x]]<sz[y]) son[x]=y;}
}int top[N],dfn[N],id;
void DFS(int x,int tp) {top[x]=tp; dfn[x]=++id;if(son[x]) DFS(son[x],tp);else return ;for(int k=last[x],y;k;k=a[k].next) {y=a[k].y;if(y^fa[x]&&y^son[x]) DFS(y,y);}
}ll c1[N],c2[N];
void add(int x,int d) {for(ll z=x*d;x<=n;x+=x&-x) c1[x]+=d,c2[x]+=z;}
void add(int x,int y,int d) {add(x,d); add(y+1,-d);}ll ask(int x) {ll y=0,z=x+1;for( ;x; x-=x&-x) y+=c1[x]*z-c2[x];return y;
}
ll ask(int l,int r) {return ask(r)-ask(l-1); }void ADD(int x) {while(x) {add(dfn[top[x]],dfn[x],2);x=fa[top[x]];}
}ll ASK(int x) {ll y=0;while(x) y+=ask(dfn[top[x]],dfn[x]),x=fa[top[x]];return y;
}int ts;
ll delta,s[N];
ll F(int x) {return delta+s[x]-1ll*ts*dep[x]+ASK(x);
}int main() {qr(T); while(T--) {qr(n); qr(m); delta=ts=len=id=0; for(int i=1;i<=n;i++) last[i]=fa[i]=c1[i]=c2[i]=s[i]=0;for(int i=1,x,y;i<n;i++) qr(x),qr(y),ins(x,y),ins(y,x);dep[1]=1; dfs(1); DFS(1,1);while(m--) {int op,x,w; qr(op); qr(x);if(op==1) qr(w),++ts,delta+=w-dep[x],ADD(x);else if(op == 2) {ll t=F(x);if(t>0) s[x] -= t;}else pr2(F(x));}}return 0;
}
D Fake News
做法1:
特判 n = 1 ∣ ∣ n = 24 n=1||n=24 n=1∣∣n=24
做法2:
容易得到 n ( n + 1 ) ( 2 n + 1 ) 6 = y 2 \dfrac {n(n+1)(2n+1)} 6=y^2 6n(n+1)(2n+1)=y2.
同时 n , n + 1 , 2 n + 1 n,n+1,2n+1 n,n+1,2n+1两两互质,所以我们只要把6在他们中间除去后判定一下是否为完全平方数即可.
int T;
ll n,a,b,c;
bool pd(ll x) {ll y=sqrt(x+1);return y*y == x;
}
ll gcd(ll x,ll y) {return !x?y:gcd(y%x,x);}int main() {qr(T); while(T--) {qr(n);a=n; b=n+1; c=2*n+1;a/=gcd(a,6);b/=gcd(b,6);c/=gcd(c,6);if(pd(a)&&pd(b)&&pd(c)) puts("Fake news!");else puts("Nobody knows it better than me!");}return 0;
}
G Topo Counting
给定一个烤肉架图.求出其拓扑序的方案数.
n ≤ 3000 n\le 3000 n≤3000.
我们把上面 2 n 2n 2n个点当做架子,下面的 ( n − 1 ) ∗ 2 n (n-1)*2n (n−1)∗2n个点当做肉片.
这个例图有点问题,就是每块肉少了2个点.
首先,有一个重要结论就是两个互不相关的子图 A , B A,B A,B,拓扑序分别有 c , d c,d c,d个.
那么合并两个子图,总拓扑序的方案数为 c ∗ d ∗ C ∣ V ( A ) ∣ + ∣ V ( B ) ∣ ∣ V ( B ) ∣ c*d*C_{|V(A)|+|V(B)|}^{|V(B)|} c∗d∗C∣V(A)∣+∣V(B)∣∣V(B)∣ .
然后如果肉和架子不连通的话方案数就很好求.
但是有这些边,所以我们就必须围绕架子的点的删除情况来考虑肉的联通,当一块肉掉下时,我们就按上面的结论求出答案.
考虑如何删除点:
无论何时,第一行都可以顺序删点.(删除即表示加入拓扑序)
当第一行剩余 < < <第二行剩余,我们可以删除第二行第首项.
同时我们还可以删除下面肉的连续若干个.
上面的讨论即所有转移要围绕的条件.
定义 f [ i ] [ j ] f[i][j] f[i][j]表示第一行剩余 i i i个,第二行剩余 j j j个 加上其附属的肉片的总方案数.
g [ i ] [ j ] g[i][j] g[i][j]表示第一二行剩余 i i i,然后下面的肉剩余 j j j个的方案数.
( ( (当一块肉分离出来的时候第一列剩余 x x x个,第二列剩余 n n n个,我们就要求一个卡特兰数 C a t x , n = ( x + n x ) − ( x + n x − 1 ) ) Cat_{x,n}=\dbinom{x+n}{x}-\dbinom{x+n}{x-1}) Catx,n=(xx+n)−(x−1x+n))(剩余1的个数 ≥ \ge ≥剩余2的个数,可以做一个把转移路径对 y = x − 1 y=x-1 y=x−1做一个对称)
可以得出以下转移:
f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i−1][j].
f [ i ] [ j ] + = g [ j ] [ n ] ( j = i + 1 ) f[i][j] += g[j][n](j=i+1) f[i][j]+=g[j][n](j=i+1).
f [ i ] [ j ] + = f [ i ] [ j − 1 ] ∗ C a t n ∗ C i + j − 1 + ( j − 1 ) ∗ 2 n 2 n f[i][j] += f[i][j-1]*Cat_n*C_{i+j-1+(j-1)*2n}^{2n} f[i][j]+=f[i][j−1]∗Catn∗Ci+j−1+(j−1)∗2n2n.
g [ i ] [ j ] = g [ i ] [ j − 1 ] + C a t j , n ∗ f i − 1 , i − 1 ∗ C 2 ∗ i − 2 + ( i − 2 ∗ 2 n + 2 n − j 2 n − j g[i][j]=g[i][j-1]+Cat_{j,n}*f_{i-1,i-1} *C_{2*i-2+(i-2*2n+2n-j}^{2n-j} g[i][j]=g[i][j−1]+Catj,n∗fi−1,i−1∗C2∗i−2+(i−2∗2n+2n−j2n−j.
总复杂度为 O ( n 2 ) O(n^2) O(n2).
#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=3010,size=1<<20,inf=2e9;//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {char c=gc; x=0; int f=1;while(!isdigit(c)){if(c=='-')f=-1; c=gc;}while(isdigit(c)) x=x*10+c-'0',c=gc;x*=f;
}
template<class o> void qw(o x) {if(x/10) qw(x/10);putchar(x%10+'0');
}
template<class o> void pr1(o x) {if(x<0)x=-x,putchar('-');qw(x); putchar(' ');
}
template<class o> void pr2(o x) {if(x<0)x=-x,putchar('-');qw(x); putchar('\n');
}int n,mod,f[N][N],g[N][N];
ll jc[2*N*N],inv[2*N*N];ll power(ll a,ll b=mod-2) {ll c=1;while(b) {if(b&1) c=c*a%mod;b /= 2; a=a*a%mod;}return c;
}
ll C(int x,int y) {if(x<0 || y<0) return 0;return jc[x]*inv[y]%mod*inv[x-y]%mod;
}
ll Cat(int x,int y) {return (C(x+y,x)-C(x+y,x-1)+mod)%mod;}
void ad(int &x,int y) {x += y; x -= (x>=mod)*mod; }int main() {qr(n); qr(mod); jc[0]=1; int m=2*n*n;for(int i=1;i<=m;i++) jc[i]=jc[i-1]*i%mod;inv[m]=power(jc[m]); for(int i=m;i>=0;i--) inv[i-1]=inv[i]*i%mod;f[0][1]=1; //f[i][j]表示第一行剩余i个,第二行剩余j个加上其附属的肉片的总方案数 for(int j=2;j<=n;j++) f[0][j]=(f[0][j-1]*Cat(n,n)%mod*C(j-1+2*(j-1)*n,2*n)%mod);for(int i=1;i<=n;i++) {for(int j=0;j<=n;j++) g[i+1][j]=((j?g[i+1][j-1]:0)+Cat(j,n)*f[i-1][i]%mod*C(2*i-1+(i-1)*2*n+n+j,n+j))%mod;for(int j=i;j<=n;j++) {f[i][j]=f[i-1][j];if(i + 1 == j) ad(f[i][j],g[j][n]);else if(i + 1 < j) ad(f[i][j],(ll)f[i][j-1]*Cat(n,n)%mod*C(i+j-1+(j-1)*2*n,2*n)%mod);}}pr2(f[n][n]); return 0;
}
可以发现这个 g g g可以去掉:
int n,mod,f[N][N],jc[2*N*N],inv[2*N*N];ll power(ll a,ll b=mod-2) {ll c=1;while(b) {if(b&1) c=c*a%mod;b /= 2; a=a*a%mod;}return c;
}
ll C(int x,int y) {if(x<0 || y<0) return 0;return (ll)jc[x]*inv[y]%mod*inv[x-y]%mod;
}
ll Cat(int x,int y) {return (C(x+y,x)-C(x+y,x-1)+mod)%mod;}
void ad(int &x,int y) {x += y; x -= (x>=mod)*mod; }int main() {qr(n); qr(mod); jc[0]=1; int m=2*n*n;for(int i=1;i<=m;i++) jc[i]=(ll)jc[i-1]*i%mod;inv[m]=power(jc[m]); for(int i=m;i>=0;i--) inv[i-1]=(ll)inv[i]*i%mod;f[0][1]=1; //f[i][j]±íʾµÚÒ»ÐÐÊ£Óài¸ö,µÚ¶þÐÐÊ£Óàj¸ö¼ÓÉÏÆ丽ÊôµÄÈâƬµÄ×Ü·½°¸Êý for(int j=2;j<=n;j++) f[0][j]=(f[0][j-1]*Cat(n,n)%mod*C(j-1+2*(j-1)*n,2*n)%mod);for(int i=1;i<=n;i++) {for(int j=i;j<=n;j++) {f[i][j]=f[i-1][j];if(i + 1 == j) {ll s=0;for(int j=0;j<=n;j++) s += Cat(j,n)*C(2*i-1+(i-1)*2*n+n+j,n+j)%mod;f[i][j]=(f[i][j]+s%mod*f[i-1][i])%mod;}else if(i + 1 < j) ad(f[i][j],(ll)f[i][j-1]*Cat(n,n)%mod*C(i+j-1+(j-1)*2*n,2*n)%mod);}}pr2(f[n][n]); return 0;
}
H Dividing
神奇的数对定义如下:
1- ( 1 , k ) (1,k) (1,k).
2- ( n , k ) (n,k) (n,k)如果是那么 ( n k , k ) (nk,k) (nk,k)也是
3- ( n , k ) (n,k) (n,k)如果是那么 ( n + k , k ) (n+k,k) (n+k,k)也是.
求 1 ≤ n ≤ N , 1 ≤ k ≤ K 1\le n\le N,1\le k\le K 1≤n≤N,1≤k≤K的方案数
可以发现每次 k k k都不会变化,所以我们可以考虑每一个 k k k的答案.
容易发现有两种情况:
1 , 1 + k , 2 + k . . . . . 1,1+k,2+k..... 1,1+k,2+k.....,
k , 2 k , 3 k . . . . k,2k,3k.... k,2k,3k.....
分类讨论,然后整除分块做即可.
int T;
ll n,k;ll calc(ll m) {ll s=0;for(ll l=1,r;l<=k&&l<=m;l=++r) {ll x=m/l; r=min(m/x,k);s+=(r-l+1)%mod*(x%mod)%mod;}return s%mod;
}int main() {qr(n),qr(k),pr2((calc(n)+calc(n-1)+(k-1)-(n-1)%mod+mod)%mod);return 0;
}
I Valuable Forests
定义一个森林/树的 v a l val val为所有点度数的平方和.
求 n n n个点的所有森林/树的 v a l val val总和.
n ≤ 5000 n\le 5000 n≤5000.
首先,我们考虑求大小为 n n n的树的 v a l val val总和(设为 v a l n val_n valn).
根据 p r u f e r prufer prufer编码,一颗无根树有 t r n = n n − 2 tr_n=n^{n-2} trn=nn−2种情况.
如果一个点的度数为 d d d,那么在 p r u f e r prufer prufer中这个点会出现 d − 1 d-1 d−1次.
则有转移: v a l i = ∑ j = 1 i j 2 ∗ ( i − 2 j − 1 ) ∗ ( i − 1 ) ( i − 2 ) − ( j − 1 ) val_i=\sum_{j=1}^i j^2*\dbinom{i-2}{j-1} *(i-1)^{(i-2)-(j-1)} vali=∑j=1ij2∗(j−1i−2)∗(i−1)(i−2)−(j−1).
然后我们设 f n , g n f_n,g_n fn,gn分别表示大小为 n n n的森林的方案数和 v a l val val总和.
考虑 n n n和多少个点( d d d)形成一棵树,得到转移:
f n = ∑ d = 0 n − 1 ( n − 1 d ) ∗ t r d + 1 ∗ f i − 1 − d f_n=\sum_{d=0}^{n-1} \dbinom{n-1}{d}*tr_{d+1}*f_{i-1-d} fn=∑d=0n−1(dn−1)∗trd+1∗fi−1−d,
g n = ∑ d = 0 n − 1 ( n − 1 d ) ∗ ( f n − 1 − d ∗ v a l d + 1 + t r d + 1 ∗ g n − 1 − d ) g_n=\sum_{d=0}^{n-1} \dbinom{n-1}{d}*\left( f_{n-1-d}*val_{d+1} + tr_{d+1}*g_{n-1-d} \right) gn=∑d=0n−1(dn−1)∗(fn−1−d∗vald+1+trd+1∗gn−1−d)(即互相贡献)
总复杂度为 O ( n 2 ) O(n^2) O(n2).
int T,mod,n,C[N][N];
ll p[N],val[N],tr[N],f[N],g[N];ll power(ll a,ll b) {ll c=1;while(b) {if(b&1) c=c*a%mod;b /= 2; a=a*a%mod;}return c;
}int main() {qr(T); qr(mod); C[0][0]=1; n=5000;for(int i=1;i<=n;i++) {C[i][0]=1;for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;}tr[1]=1; p[0]=1; f[0]=f[1]=1;for(int i=2;i<=n;i++) {tr[i]=power(i,i-2);for(int j=1;j<=i-2;j++) p[j]=p[j-1]*(i-1)%mod;for(int j=1;j<i;j++) val[i] += (ll)j*j*C[i-2][j-1]%mod*p[(i-2)-(j-1)]%mod;val[i] = val[i]%mod*i%mod;//大小为i的树的val总和for(int j=0;j<i;j++) f[i]=(f[i]+C[i-1][j]*tr[j+1]%mod*f[i-1-j])%mod,g[i]=(g[i]+C[i-1][j]*(tr[j+1]*g[i-1-j]%mod+f[i-1-j]*val[j+1]%mod)%mod)%mod;}while(T--) qr(n),pr2(g[n]);return 0;
}
J Pointer Analysis
指针分析.
需要注意 a ∼ z a\sim z a∼z 是对象, a . ∗ , A , A . ∗ a.*,A,A.* a.∗,A,A.∗都是指针.
我们对每一个指针记录其可指向的对象,可以考虑状压指向的对象.
char l[210][5],r[210][5];
int n,f[N*N],id[150],tot,s;int main() {for(int i='a';i<='z';i++) id[i]=tot,tot+=26;for(int i='A';i<='Z';i++) id[i]=tot++;qr(n); for(int i=1;i<=n;i++) scanf("%s = %s",l[i],r[i]);while(1) {for(int i=1;i<=n;i++) {int a=id[l[i][0]],b=id[r[i][0]];if(islower(r[i][0])) f[a] |= 1<<(r[i][0]-'a');else {if(!l[i][1]) {if(!r[i][1]) f[a] |= f[b];else {for(int j=0;j<26;j++)if(f[b]>>j&1) f[a] |= f[id[j+'a']+r[i][2]-'a'];}}else {for(int j=0;j<26;j++)if(f[a]>>j&1) f[id[j+'a']+l[i][2]-'a'] |= f[b];}}}int last=s; s=0;for(int i=0;i<tot;i++) s += f[i];if(last==s) break;}for(int i='A';i<='Z';i++) {printf("%c: ",i);for(int j=0;j<26;j++)if(f[id[i]]>>j&1) putchar(j+'a');puts("");}
}