CSP2024 前集训:多校A层冲刺NOIP2024模拟赛05
前言
一次模拟赛四个计数,后三道一个 trick,yl 他妈都能被气活:
T2、T3、T4 全是正难则反的 trick,T3、T4 都需要回退背包,T1 也可以背包。
T2 没想到正难则反一直死磕心态炸了,其实要试看看 T3 搞不好还想出来了。
以后再想不到正难则反记得抽死我谢谢。
T1 好数
可以直接暴力背包套 bitset 操过去,复杂度 \(O(\dfrac{nv}{w})\),\(v\) 是值域。
也可以移项 \(a+b+c=d\to a+b=d-c\) 开桶 \(O(n^2)\) 维护。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=5010,M=6e5+10,V=1e5;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') z=0;
for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,ans,a[N]; bitset<M>f[5];
signed main()
{
freopen("number.in","r",stdin),freopen("number.out","w",stdout);
read(n); for(int i=1;i<=n;i++) read(a[i]),a[i]+=V;
f[0][0]=1; for(int i=1;i<=n;i++)
{
ans+=f[3][a[i]+2*V];
for(int j=1;j<=3;j++) f[j]|=(f[j-1]<<a[i]);
}
write(ans);
}
T2 SOS字符串
正难则反,\(26^n\) 减去 SOS
个数 \(\le 2\) 的,DP 直接转移即可。
赛时一直正着推,没想到就算 SOS
数量固定也会有算重的,遂 \(n\le 12\) 都是错的。
所以以后先把暴力打出来方便拍,还有组合数搞不出来的时候想一想 DP。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e6+10,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') z=0;
for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,ans=1,f[2][3][3];
signed main()
{
freopen("sos.in","r",stdin),freopen("sos.out","w",stdout);
read(n); f[0][0][0]=1;
for(int i=1,x=1,y=0;i<=n;i++,x^=1,y^=1,ans=26ll*ans%P)
for(int j=0;j<=2;j++)
{
f[x][j][0]=25ll*(f[y][j][0]+f[y][j][2])%P;
(f[x][j][0]+=24ll*f[y][j][1]%P)%=P;
if(j) (f[x][j][0]+=f[y][j-1][2])%=P;
f[x][j][1]=(f[y][j][0]+f[y][j][1])%P;
f[x][j][2]=f[y][j][1];
}
for(int i=0,x=n&1;i<=2;i++)
for(int j=0;j<=2;j++) (ans+=P-f[x][i][j])%=P;
write(ans);
}
T3 集训营的气球
一样的 trick 正难则反,然后就是回退背包板子。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e6+10,M=20,P=1e9+7;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,c,m,a[N],b[N]; ll ans=1,f[M];
int inv(ll a,int b=P-2)
{ll res=1; for(;b;(a*=a)%=P,b>>=1) if(b&1) (res*=a)%=P; return res;}
signed main()
{
freopen("balloon.in","r",stdin),freopen("balloon.out","w",stdout);
read(n,c); f[0]=1;
for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++) read(b[i]);
for(int i=1;i<=n;(f[0]=f[0]*b[i]%P)%=P,(ans*=a[i]+b[i])%=P,i++)
for(int j=c-1;j>=1;j--) f[j]=(f[j-1]*a[i]%P+f[j]*b[i]%P)%P;
read(m); for(int i=1,o,x,y,sum,va,vb;i<=m;i++)
{
read(o,x,y); va=inv(a[o]),vb=inv(b[o]),sum=0;
(ans*=1ll*inv(a[o]+b[o])*(x+y)%P)%=P,(f[0]*=vb)%=P;
for(int j=1;j<c;j++) f[j]=(f[j]-f[j-1]*a[o]%P+P)*vb%P;
for(int j=c-1;j>=1;j--) (sum+=(f[j]=(f[j-1]*x%P+f[j]*y%P)%P))%=P;
a[o]=x,b[o]=y; write((ans-sum-((f[0]*=y)%=P)+(P<<1))%P),puts("");
}
}
T4 连通子树与树的重心
先求出原树的重心,可能有一个或两个,分类讨论。
两个重心
这个比较容易处理,设两个重心分别为 \(u,v\),必定存在边 \((u,v)\),从而分别以两个重心为根跑一遍树形背包。
设 \(f_{x,i}\) 表示以 \(x\) 为根且块内节点数量为 \(i\) 的连通块的个数,有转移:
最后答案为 \(\sum\limits_{i=1}^{\max(sz_u,sz_v)}f_{u,i}\times f_{v,i}\)。
-
关于本题树形背包的复杂度:
转移等价于找出一组 \(x,y\) 给 \(lca(x,y)\) 做出贡献,遂每组 \(x,y\) 只可能被记到一次,遂复杂度为 \(O(n^2)\)。
一个重心
先以重心为根跑一遍上面的 DP,然后考虑容斥,依然是正难则反。
求出 \(f_{x,i}\) 中最大子树 \(>\dfrac{i}{2}\) 的即可,回退背包。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define pb push_back
using namespace std;
const int N=5010,P=10007;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,ans,rt[3],sz[N],mx[N],f[N][N],g[N][N];
vector<int>e[N];
void getrt(int x,int t)
{
sz[x]=1; for(int y:e[x]) if(y!=t)
getrt(y,x),sz[x]+=sz[y],mx[x]=max(mx[x],sz[y]);
mx[x]=max(mx[x],n-sz[x]); if(mx[x]<=(n>>1)) rt[++rt[0]]=x;
}
void dfs(int x,int t)
{
sz[x]=1,f[x][0]=f[x][1]=1; for(int y:e[x]) if(y!=t)
{
dfs(y,x),sz[x]+=sz[y];
for(int i=sz[x];i;i--) for(int j=1;j<=min(sz[y],i-1);j++)
(f[x][i]+=f[x][i-j]*f[y][j]%P)%=P;
}
}
signed main()
{
freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
read(n); for(int i=1,x,y;i<n;i++) read(x,y),e[x].pb(y),e[y].pb(x);
getrt(1,0); if(!(rt[0]&1))
{
cerr<<1<<endl;
dfs(rt[1],rt[2]),dfs(rt[2],rt[1]);
for(int i=1;i<=n;i++) (ans+=f[rt[1]][i]*f[rt[2]][i]%P)%=P;
return write(ans),puts(""),0;
}
int x=rt[1]; dfs(x,0); for(int i=1;i<=n;i++) (ans+=f[x][i])%=P;
for(int y:e[x]) for(int i=1;i<=n;i++)
{
g[y][i]=f[x][i]; for(int j=1;j<=min(i,sz[y]);j++)
(g[y][i]+=P-g[y][i-j]*f[y][j]%P)%=P;
}
for(int y:e[x]) for(int i=1;i<=n;i++)
for(int j=(i+1)/2;j<=min(i,sz[y]);j++)
(ans+=P-f[y][j]*g[y][i-j]%P)%=P;
write(ans),puts("");
}