没啥好说的直接 \(O(n^2)\) 枚举即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+107;
const int d=2e5;
int n,a[N],sum[N];
int read()
{
int f=1,s=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return f*s;
}
signed main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=read();
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i-1;j++)
{
int x=a[i]-a[j]+d;
if(sum[x]==1)
{
ans++;
break;
}
}
for(int j=1;j<=i;j++) sum[a[i]+a[j]+d]=1;
}
printf("%d",ans);
}
可DP,可分讨,我写的分讨,其实DP也是那个流程。如果DP,要正难则反,直接统计小于3个的情况,最后用 \(26^n\) 减去即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e6+107;
const int mod=1e9+7;
int n,ans;
int f[N][10];
int read()
{
int f=1,s=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return f*s;
}
int dfs(int len,int k)
{
if(f[len][k]!=-1) return f[len][k];
if(len==0)
{
if(k==0) return f[len][k]=1;
else return f[len][k]=0;
}
int res=0;
if(k==9) // 9: ' *SOS*SOS*SOS* '
{
(res+=dfs(len-1,8))%=mod;//*SOS*SOS*SO + S
(res+=dfs(len-1,9)*26)%=mod;//*SOS*SOS*SOS* + A-Z
}
if(k==8)// 8: ' *SOS*SOS*SO '
{
(res+=dfs(len-1,7))%=mod;//*SOS*SOS*S + O
}
if(k==7)// 7: ' *SOS*SOS*S '
{
(res+=dfs(len-1,6))%=mod;//*SOS*SOS* + S
(res+=dfs(len-1,7))%=mod;//*SOS*SOS(*S) + S
}
if(k==6)// 6: ' *SOS*SOS* '
{
(res+=dfs(len-1,5))%=mod;//*SOS*SO + S
(res+=dfs(len-1,6)*25)%=mod;//*SOS*SOS* + A-Z - S
(res+=dfs(len-1,7)*24)%=mod;//*SOS*SOS(*S) + A-Z -S(k==7) -O(k==8)
(res+=dfs(len-1,8)*25)%=mod;//*SOS*SOS(*SO) + A-Z - S(k==9)
}
if(k==5)// 5: ' *SOS*SO '
{
(res+=dfs(len-1,4))%=mod;//*SOS*S + O
}
if(k==4)// 4: ' *SOS*S '
{
(res+=dfs(len-1,3))%=mod;//*SOS* + S
(res+=dfs(len-1,4))%=mod;//*SOS(*S) + S
}
if(k==3)// 3: ' *SOS* '
{
(res+=dfs(len-1,2))%=mod;//*SO + S
(res+=dfs(len-1,3)*25)%=mod;//*SOS* + A-Z -S(k==4)
(res+=dfs(len-1,4)*24)%=mod;//*SOS*S + A-Z -S(k==4) -O(k==5)
(res+=dfs(len-1,5)*25)%=mod;//*SOS*SO + A-Z -S(k==6)
}
if(k==2)// 2: ' *SO '
{
(res+=dfs(len-1,1))%=mod;//*S + O
}
if(k==1)// 1: ' *S '
{
(res+=dfs(len-1,0))%=mod;//* + S
(res+=dfs(len-1,1))%=mod;//(*S) + S
}
if(k==0)// 0: ' * '
{
(res+=dfs(len-1,0)*25)%=mod;//* + A-Z - S(k==1)
(res+=dfs(len-1,1)*24)%=mod;//* + A-Z - S(k==1) - O(k==2)
(res+=dfs(len-1,2)*25)%=mod;//* + A-Z -S(k==3)
}
return f[len][k]=res%mod;
}
// 0: ' * '
// 1: ' *S '
// 2: ' *SO '
// 3: ' *SOS* '
// 4: ' *SOS*S '
// 5: ' *SOS*SO '
// 6: ' *SOS*SOS* '
// 7: ' *SOS*SOS*S '
// 8: ' *SOS*SOS*SO '
// 9: ' *SOS*SOS*SOS* '
int res[N];
signed main()
{
freopen("sos.in","r",stdin);
freopen("sos.out","w",stdout);
n=read();
memset(f,-1,sizeof f);
dfs(n,9);
printf("%lld\n",f[n][9]%mod);
return 0;
}
正难则反,首先我们可以想到直接跑01背包,然后统计小于 \(c\) 的情况,复杂度是 \(nc\) ,关键是修改操作,如何动态修改,这里有一个新的技巧:退背包(可能是我太菜了,第一次听到),每次修改的时候先退背包,再正常跑一遍的DP,然后就没啥了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e6+107;
const int p=1e9+7;
int n,c,q;
int a[N],b[N];
int f[N],sum;
int read()
{
int f=1,s=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return f*s;
}
int qpow(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=ans*a%p;
a=a*a%p;
b=b>>1;
}
return ans;
}
signed main()
{
freopen("balloon.in","r",stdin);
freopen("balloon.out","w",stdout);
n=read(),c=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=read();
f[0]=1,sum=1;
for(int i=1;i<=n;i++)
{
sum=sum*(a[i]+b[i])%p;
for(int j=c-1;j>=1;j--)
{
f[j]=(f[j-1]*a[i]%p+f[j]*b[i]%p)%p;
}
f[0]=f[0]*b[i]%p;
}
int q=read();
for(int i=1;i<=q;i++)
{
int pos=read(),x=read(),y=read();
sum=sum*qpow(a[pos]+b[pos],p-2)%p*(x+y)%p;
int inv=qpow(b[pos],p-2);
f[0]=f[0]*inv%p;
for(int j=1;j<=c-1;j++) f[j]=(f[j]-f[j-1]*a[pos]%p+p)*inv%p;
for(int j=c-1;j>=1;j--) f[j]=(f[j-1]*x%p+f[j]*y%p)%p;
f[0]=f[0]*y%p;
int res=sum;
for(int j=0;j<c;j++) res=(res-f[j]+p)%p;
a[pos]=x,b[pos]=y;
printf("%lld\n",res);
}
}
先求出树的重心,这里是会有两种情况:一个重心和两个重心。
我们先来设计一下DP,设 \(f[i][j]\) 表示以 \(i\) 为根的子树选了 \(j\) 个节点相互连通的方案数。其中这个 \(f\) ,我们可以通过树形背包DP来得到。\(f[u][i]+=f[v][j]*f[u][i-j]\)
那先来看有两个重心的情况,分析一下,首先连接两个重心的边肯定不能断,且两个重心除了相互连通外要有同样个数的节点与它相连。
那比较好解决,我们直接断边每个重心跑一遍DP,最后答案为 \(\sum_{i=1}^{n}f[c1][i]*f[c2][i]\)
接着来分析只有一个重心的时候,比较好像到的是,再跑一遍DP,我们设 \(g[i][j]\) 表示以 \(c\) 为根,大小为 \(i\) 的一个连通块中子树大小为 \(j\) 的方案数。由重心的性质:重心所有的子树大小不超过 \(n/2\) 可知,最后答案应为 \(\sum_{i,j*2<=i}\) 。
但这样的复杂度为 \(n^3\) 无法接受,我们考虑换个方法,考虑正难则反(真服了,一场模拟赛放三这玩意),我们只需要用总方案减去那些子树节点大于 \(i/2\) 的情况。如果我们直接用 \(f\) 数组很明显不对,因为如果我们制定任意一个子树 \(v\) 的节点个数大于 \(i/2\) 为 \(j\) ,那此时 \(f[u][i-j]\) 因为考虑了 \(v\) 这个子树而会算重。这里我们就需要容斥一下,还是考虑退背包操作(这场模拟赛没话说),这个直接根据动态转移方程推一下,还是比较好说的,剩下的就没了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5000+107;
const int mod=10007;
int n;
int f[N][N],g[N][N];
int read()
{
int f=1,s=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return f*s;
}
int h[N<<3],to[N<<3],nxt[N<<3],tot;
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=h[x];
h[x]=tot;
}
int sz[N],w[N],c[3],cnt;
void dfs(int u,int fa)
{
sz[u]=1,w[u]=0;
for(int i=h[u];i;i=nxt[i])
{
int v=to[i];
if(v!=fa)
{
dfs(v,u);
sz[u]+=sz[v];
w[u]=max(w[u],sz[v]);
}
}
w[u]=max(w[u],n-sz[u]);
if(w[u]<=n/2) c[cnt++]=u;
}
void dp(int u,int fa)
{
sz[u]=1;
f[u][0]=f[u][1]=1;
for(int x=h[u];x;x=nxt[x])
{
int v=to[x];
if(v==fa) continue;
dp(v,u);
sz[u]+=sz[v];
for(int i=sz[u];i>=0;i--)
{
for(int j=1;j<=sz[v]&&j<=i-1;j++)
{
(f[u][i]+=f[v][j]*f[u][i-j])%=mod;
}
}
}
}
void solve2()
{
dp(c[0],c[1]);
dp(c[1],c[0]);
int ans=0;
for(int i=1;i<=n;i++)
{
(ans+=f[c[0]][i]*f[c[1]][i]%mod)%=mod;
}
printf("%lld",ans);
}
void solve1()
{
int u=c[0],ans=0;
dp(u,0);
for(int i=1;i<=n;i++) (ans+=f[u][i])%=mod;
for(int i=h[u];i;i=nxt[i])
{
int v=to[i];
for(int j=1;j<=n;j++)
{
g[v][j]=f[u][j];
for(int k=1;k<=min(j,sz[v]);k++)
{
g[v][j]=(g[v][j]-g[v][j-k]*f[v][k]%mod+mod)%mod;
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=h[u];j;j=nxt[j])
{
int v=to[j];
for(int k=(i+1)/2;k<=i&&k<=sz[v];k++)
{
ans=(ans-f[v][k]*g[v][i-k]%mod+mod)%mod;
}
}
}
printf("%lld\n",ans);
}
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();
for(int i=1;i<=n-1;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
}
dfs(1,0);
if(c[1]) solve2();
else solve1();
return 0;
}