T1 NS
某道没做出来的题的加强版。
然后这个序列要构造出来,
定义
Fi,j
为长度为
i
以
发现将整个序列大于等于某个数的一部分增加1,
是仍然符合这个序列之前的要求的,
所以我们可以写一个
O(n3)
的DP,
然后差分优化。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define Mod 1000000007
#define M 1004
#define Add(a,b) (a+=b)%=Mod
int n;
char S[M];
int F[M][M];
int main(){
while(scanf("%s",S+1)!=EOF){
n=strlen(S+1);
memset(F,0,sizeof(F));
F[0][1]=1;
REP(i,1,n+1){
int Val=0;
REP(j,1,i+1){
Add(Val,F[i-1][j]);
if(S[i]=='D') Add(F[i][1],Val),Add(F[i][j+1],Mod-Val);
else if(S[i]=='I') Add(F[i][j+1],Val);
else Add(F[i][1],Val);
}
}
int Ans=0,Val=0;
REP(i,1,n+2){
Add(Val,F[n][i]);
Add(Ans,Val);
}
printf("%d\n",Ans);
}
return 0;
}
T2 HJ
比较正常的计数题。
用到一种方法将连着的相同颜色的边不做处理。
令颜色相同的边连续地加入,即可将颜色一维转换为时间维。
即可分为两种情况讨论。
然后算DP就比较正常了。
容斥的地方写错调了一会。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
#define M 300004
#define N 100004
int n,Val[M];
int Next[M<<1],V[M<<1],C[M<<1],Head[M],tot;
void Add_Edge(int u,int v,int c){
Next[++tot]=Head[u],V[Head[u]=tot]=v,C[tot]=c;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
//定义F[A]为由A向上的合法路径的条数。
//定义G[A]为由A向上的合法路径的权值之和。
//考虑每一条路径经过A。
LL G[M],F[M],Ans;
void DFS(int A,int f,int fc){
int B;
LL Fp=0,Gp=0;
int Lastc=0;
F[A]=1;
G[A]=Val[A];
LREP(i,A)if((B=V[i])!=f){
int c=C[i];
DFS(B,A,c);
if(c!=Lastc) G[A]+=Gp,F[A]+=Fp,Gp=Fp=0,Lastc=c;
Ans+=G[A]*F[B]+G[B]*F[A];
Gp+=G[B]+F[B]*Val[A],Fp+=F[B];
}
G[A]+=Gp,F[A]+=Fp;
LREP(i,A)if((B=V[i])!=f){
int c=C[i];
if(c==fc)F[A]-=F[B],G[A]-=G[B]+F[B]*Val[A];
}
}
vector<int>X[N],Y[N];
#define pb push_back
#define SZ(a) ((int)(a).size())
int main(){
while(scanf("%d",&n)!=EOF){
memset(Head,tot=0,sizeof(Head));
REP(i,1,N)X[i].clear(),Y[i].clear();
REP(i,1,n+1)scanf("%d",&Val[i]);
REP(i,1,n){
int u,v,c;
scanf("%d %d %d",&u,&v,&c);
X[c].pb(u),Y[c].pb(v);
}
REP(c,1,N) REP(i,0,SZ(X[c])){
int u=X[c][i],v=Y[c][i];
Add_Edge(u,v,c);
Add_Edge(v,u,c);
}
memset(F,0,sizeof(F));
memset(G,0,sizeof(G));
Ans=0;
DFS(1,0,0);
printf("%lld\n",Ans);
}
return 0;
}
T3 TD
ACM比赛里只有8人弄出来的题,RT
枚举放两个重塔的行和列,那么被删掉的行列都是可知的。
然后排列组合算出这样的方案数为:
对于每行:
Cn,i∗Cm,2∗i∗(2∗i)!2i
然后在删掉这些行列基础上算列:
Cn−i,2∗j∗Cm−2∗i,j∗(2∗j)!2j
那么问题就转换为了只放“轻塔”的情况。
然后枚举放了
k
个这样的塔,
那么方案数是
然而重塔和轻塔此时还是没区分开,
考虑此时重塔最少和最多出现的个数。
这部分的方案数是一个累计的组合数,前缀和处理即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
#define M 204
#define Mod 1000000007
#define Add(a,b) (a+=b)%=Mod
#define Mul(a,b) (1ll*(a)*(b)%Mod)
#define C2(a) ((a)*((a)-1)>>1)
int T,n,m,q,p;
int C[M][M],Sum[M][M],Fac[M<<1],Inv[M];
int Pow(int x,int p){
int res=1,k=x;
for(;p;k=Mul(k,k),p>>=1)if(p&1)res=Mul(res,k);
return res;
}
int main(){
REP(i,0,M-1){
C[i][0]=Sum[i][0]=1;
REP(j,1,i+1)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod;
REP(j,1,M) Sum[i][j]=(Sum[i][j-1]+C[i][j])%Mod;
}
Fac[0]=Inv[0]=1;
REP(i,1,M<<1)Fac[i]=Mul(i,Fac[i-1]);
REP(i,1,M)Inv[i]=(Inv[i-1]<<1)%Mod;
REP(i,1,M)Inv[i]=Pow(Inv[i],Mod-2);
scanf("%d",&T);
while(T--){
scanf("%d %d %d %d",&n,&m,&p,&q);
int Val=1,Ans=0;
REP(i,0,n+1) REP(j,0,m+1){
int i2=i<<1,j2=j<<1;
if(i2+j2>p || i+j2>n || i2+j>m)break;
int Val,x=n-i-j2,y=m-i2-j;
Val=Mul(Mul(C[n][i],C[m][i2]),Mul(Fac[i2],Inv[i]) );
Val=Mul(Val,Mul(Mul(C[m-i2][j],C[n-i][j2]),Mul(Fac[j2],Inv[j])));
int Res=0;
REP(k,0,p+q-i2-j2+1){
if(k>x || k>y)break;
int minp=max(0,k-q),maxp=min(k,p-i2-j2);
Add(Res,Mul(Mul(Fac[k],Mul(C[x][k],C[y][k])),
(Sum[k][maxp]+Mod-(minp?Sum[k][minp-1]:0))%Mod));
}
Add(Ans,Mul(Val,Res));
}
Add(Ans,Mod-1);
printf("%d\n",Ans);
}
return 0;
}
T4 K
状压DP以及枚举子集。
这里看起来有一个图的生成树计数。
不过可以直接用状压枚举子集来容斥写,
注意一下重复,严格包含某个元素即可
其实复杂度比那个生成树计数要小
然后就是把几个连通块拼在一起得到结果,
排列数目重复,除去
k!
即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
#define Mod 1000000009
#define M 24
#define Add(a,b) ((a+=b)%=Mod)
#define Mul(a,b) (1ll*(a)*(b)%Mod)
int T,E[M],n,m,K;
int F[(1<<14)+4][15];
int P[(1<<14)+4],Cnt[(1<<14)+4],C[M][M];
int Inv[M];
int Pow(int x,int p){
int res=1,k=x;
for(;p;k=Mul(k,k),p>>=1)if(p&1)res=Mul(res,k);
return res;
}
int main(){
REP(i,0,M-1){
C[i][0]=1;
REP(j,1,i+1)C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod;
}
Inv[0]=1;
REP(i,1,M)Inv[i]=1ll*Inv[i-1]*i%Mod;
REP(i,2,M)Inv[i]=Pow(Inv[i],Mod-2);
scanf("%d",&T);
REP(Case,1,T+1){
scanf("%d %d %d",&n,&m,&K);
memset(E,0,sizeof(E));
while(m--){
int u,v;
scanf("%d%d",&u,&v);
u--,v--;
E[u]|=1<<v;
E[v]|=1<<u;
}
memset(F,0,sizeof(F));
memset(P,0,sizeof(P));
memset(Cnt,0,sizeof(Cnt));
int Mxt=(1<<n)-1;
REP(i,1,Mxt+1){
Cnt[i]=1;
REP(j,0,n)REP(k,j,n)if( (i&(1<<j)) && (i&E[j]&(1<<k)) ) Add(Cnt[i],Cnt[i]);
P[i]=Cnt[i];
int p;
REP(j,0,n)if(i&(1<<j)){p=j;break;}
for(int j=(i-1)&i;j;j=(j-1)&i)if(j&(1<<p))
Add(P[i],Mul(Cnt[i^j],Mod-P[j]));
}
int Val;
F[0][0]=1;
REP(k,0,K) REP(i,0,Mxt)if(Val=F[i][k])
for(int p=i^Mxt,j=p;j;j=(j-1)&p)
Add(F[i|j][k+1],Mul(Val,P[j]));
printf("Case #%d:\n%lld\n",Case,Mul(F[Mxt][K],Inv[K]));
}
return 0;
}