又挂了60分
T1:求一棵树的点分治方案数。
状压
分,一条链的特殊情况
分。
但是我预处理对数(
,状压DP要用)的时候,不小心在开头预处理了,
所以链的情况就全部
。。。。。
正解是考虑如何让这个问题可以树形
。
树形
的关键在于合并两个连通块的答案。
那么考虑断开这两个连通块间的一条边后,我们得到了点分树的两个子树。
如果我们把两棵树通过⼀条边 连接起来, 我们怎样把原有的两棵点分树合并起来,并且保持原本点分树中 的祖先后代关系呢?可以发现,我们只要把
到所在点分树根节 点的路径以及
到所在点分树根节点的路径按任意顺序归并起来即可。
这个归并就是一个组合数,那么我们就可以合并两个连通块的答案了,直接树形DP即可。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define fi first
#define se second
#define pb push_back
#define rep(i,a,b) for(int i=(a),LIM=(b);i<=LIM;i++)
#define per(i,a,b) for(int i=(a),LIM=(b);i>=LIM;i--)
const int N=5e3+5,mod=1e9+7;
int n;
int f[N][N];
int t[N],h[N],sz[N],C[N][N];
vector<int>G[N];
void dfs(int u,int p){
for(int v:G[u]) if(v^p)
dfs(v,u);
sz[u]=1;
f[u][1]=1;
for(int v:G[u]) if(v^p){
memset(h,0,4*(sz[u]+sz[v]+1));
per(i,sz[v],0) h[i]=(h[i+1]+f[v][i])%mod;
rep(i,1,sz[u]+sz[v]){
rep(j,max(1,i-sz[v]),min(i,sz[u])){
t[i]=(t[i]+f[u][j]*1ll*C[i-1][j-1]%mod*h[i-j])%mod;
}
}
sz[u]+=sz[v];
memcpy(f[u],t,4*(sz[u]+1)),memset(t,0,4*(sz[u]+1));
}
}
int main(){
scanf("%d",&n);
C[0][0]=1;
rep(i,1,n)
rep(j,C[i][0]=1,i)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
rep(i,1,n-1){
int u,v;
scanf("%d%d",&u,&v);
G[u].pb(v),G[v].pb(u);
}
dfs(1,0);
int ans=0;
rep(i,1,n)
ans=(ans+f[1][i])%mod;
printf("%d\n",ans);
}
T2:身份证
Z国近日启用了新的身份证系统,每个人的身份证上会有三个字符串。
小猪佩奇正在对这个系统进行测试,这个系统有三个输入框,每个框内可以输入一个字符串,系统会返回输入的三个串分别是该人身份证上三个字符串的前缀的人数。
三个输入框开始都为空,小猪佩奇每次会在一个输入框中键入一个字符,或删除末尾的字符,系统会实时返回结果。为了确认系统返回的结果是否正确,她希望你写一个功能相同的程序进行验证。
50分
打完就走。
考后发现这是一道思博题。
考虑对每个⼈的三个串分别建trie,在三个串的末端插⼊这个⼈,那么我们每次就是要询问三个trie中三个⼦树中的⼈的交集
⼤⼩。 如果我们在每个⼈第⼀个串trie上到根的那条链上全部插⼊这个⼈,我们就是要询问后两个trie中某两⼦树中的⼈与
第⼀个trie中某个点上的⼈的交集⼤⼩。把第⼀个trie上每个点的询问离线出来,在后两个trie的dfs序做⼀个⼆维偏序即可,
这个可以排序之后扫描线树状数组。
注意字符总数是
的所以我们可以把第一个
上某个点子树内的所有人都拎出来跑二维偏序。
#include<bits/stdc++.h>
#define maxn 500005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;
int n,tot,cnt_p,t,rt[3],S[3],tr[maxn*2][26],fa[maxn*2],ps[maxn][3],pl[maxn][3],dfn[maxn],dfnr[maxn];char str[3][maxn];
vector<int>Rt[maxn],G[maxn];
inline void ins(int o,int id){
int x=rt[o],l=strlen(str[o]+1);
rep(i,1,l){
int c=str[o][i]-'a';
if(!tr[x][c])tr[x][c]=++tot,fa[tot]=x;
x=tr[x][c];
}
ps[id][o]=x;
}
void dfs(int x){
dfn[x]=++t;int y;
rep(i,0,25) if(y=tr[x][i])dfs(y);
dfnr[x]=t;
}
struct node{
int l,r,s;
}T[maxn*20];
void upd(int&x,int y,int l,int r,int p){
T[x=++cnt_p]=T[y];T[x].s++;
if(l==r) return;
int m=l+r>>1;
p<=m?upd(T[x].l,T[y].l,l,m,p):upd(T[x].r,T[y].r,m+1,r,p);
}
int qry(int x,int y,int l,int r,int tl,int tr){
if(T[y].s==T[x].s)return 0;
if(tl<=l&&r<=tr)return T[y].s-T[x].s;
int m=l+r>>1;
if(tr<=m)return qry(T[x].l,T[y].l,l,m,tl,tr);
if(tl>m)return qry(T[x].r,T[y].r,m+1,r,tl,tr);
return qry(T[x].l,T[y].l,l,m,tl,tr)+qry(T[x].r,T[y].r,m+1,r,tl,tr);
}
inline bool cmp(int x,int y){return pl[x][0]<pl[y][0];}
void Build(int x){
G[x].push_back(0);sort(G[x].begin(),G[x].end(),cmp);
Rt[x].resize(G[x].size());Rt[x][0]=0;
for(int i=1;i<G[x].size();i++)upd(Rt[x][i],Rt[x][i-1],1,S[2],pl[G[x][i]][2]);
for(int i=1;i<G[x].size();i++)G[x][i]=pl[G[x][i]][0];
}
void Qry(int x,int y,int z){
int yl=dfn[y],yr=dfnr[y],zl=dfn[z],zr=dfnr[z];
int p1=lower_bound(G[x].begin(),G[x].end(),yr+1)-G[x].begin()-1;
int p2=lower_bound(G[x].begin(),G[x].end(),yl)-G[x].begin();
if(p2>p1){puts("0");return;}
int res=qry(Rt[x][p2-1],Rt[x][p1],1,S[2],zl,zr);
printf("%d\n",res);
}
int main()
{
scanf("%d",&n);tot=3;rt[0]=1;rt[1]=2;rt[2]=3;
rep(i,1,n){
scanf("%s%s%s",str[0]+1,str[1]+1,str[2]+1);
ins(0,i);ins(1,i);ins(2,i);
}
rep(o,0,2){
t=0;dfs(rt[o]);S[o]=t;
for(int i=1;i<=n;i++)pl[i][o]=dfn[ps[i][o]];
}
rep(i,1,n) for(int x=ps[i][1];x;x=fa[x])G[dfn[x]].push_back(i);
rep(i,1,S[1]) if(G[i].size())Build(i);
int Q,pos[3];scanf("%d",&Q);
rep(i,0,2) pos[i]=rt[i];
while(Q--){
int o;char s[5];scanf("%s%d",s,&o);o--;
if(s[0]=='+'){
scanf("%s",s);int c=s[0]-'a';
if(!tr[pos[o]][c])tr[pos[o]][c]=++tot,fa[tot]=pos[o];
pos[o]=tr[pos[o]][c];
}
else pos[o]=fa[pos[o]];
if(!dfn[pos[0]]||!dfn[pos[1]]||!dfn[pos[2]]){puts("0");continue;}
Qry(dfn[pos[1]],pos[0],pos[2]);
}
return 0;
}
T3:Fib与gcd
次询问
考场上莫名其妙的想出了正解,在疯狂猜结论之后得到了一个看似不靠谱的做饭但是对拍了半小时没问题,就没管了。
然后被鬼畜出题人多组数据卡掉50分。5555555555555555
最后发现是有一个特判没打。。。。。。
具体做法就是把
消掉,然后对剩下的
个特判掉一些情况后,用模
在和
求
之类的技巧组合出
。
#include<bits/stdc++.h>
#define Ct const
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define LL long long
#define mod 998244353
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
template<class T>void read(T &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
int P;
struct mat{
int a[2][2];
mat (int d=0){ memset(a,0,sizeof a);rep(i,0,1) a[i][i] = d; }
mat operator *(Ct mat &b)Ct{
mat r;
r.a[0][0]=(1ll*a[0][0]*b.a[0][0]+1ll*a[0][1]*b.a[1][0])%P;
r.a[0][1]=(1ll*a[0][0]*b.a[0][1]+1ll*a[0][1]*b.a[1][1])%P;
r.a[1][0]=(1ll*a[1][0]*b.a[0][0]+1ll*a[1][1]*b.a[1][0])%P;
r.a[1][1]=(1ll*a[1][0]*b.a[0][1]+1ll*a[1][1]*b.a[1][1])%P;
return r;
}
}A;
mat Pow(mat b,LL k){
mat r(1);for(;k;k>>=1,b=b*b) if(k&1) r=r*b;return r;
}
int F(LL n,LL md=mod){
if(n == 0) return 0;
P = md;
mat r = Pow(A,n-1);
return r.a[0][0];
}
LL gcd(LL a,LL b){ return !b ? a : gcd(b,a%b); }
int main(){
A.a[0][0] = A.a[1][0] = A.a[0][1] = 1;
int q;read(q);
for(;q--;){
LL n,a,b,c,d;
read(n),read(a),read(b),read(c),read(d);
if(n <= 40){
printf("%lld\n",((gcd(a*F(n)+b*F(n+1),c*F(n)+d*F(n+1)))%mod+mod)%mod);
continue;
}
if(b==0 && d==0){
printf("%lld\n",((gcd(a,c)*F(n))%mod+mod)%mod);
continue;
}
if(b==0 && c==0){
printf("%lld\n",gcd(a,F(n+1,a)*d) * gcd(d,F(n,d)*a) / gcd(a,d));
continue;
}
for(;c;){
swap(a,c),swap(b,d);
LL t = c / a;
c -= t * a , d -= b * t;
}
if(d < 0) d = -d;
if(a < 0) a = -a , b = -b;
if(d == 0){
printf("%lld\n",((a*F(n)+b*F(n+1))%mod+mod)%mod);
continue;
}
if(a == 0){
printf("%lld\n",((gcd(b,d)*F(n+1))%mod+mod)%mod);
continue;
}
b = (b % d + d)%d;
LL g1 = gcd(a,F(n+1,a)*d) * gcd(d,F(n,d)*a) / gcd(a,d);
g1 = gcd(g1 , F(n+1,g1) * gcd(b,d));
LL g2 = g1 / gcd(g1 , F(n+1,g1)) , g3 = g1 * (d / g2);
printf("%lld\n",(gcd(g3,a*F(n,g3)+b*F(n+1,g3)) % mod + mod) % mod);
}
}