bzoj2302: [HAOI2011]Problem c
脑子是个好东西,可惜我没有
容易发现只要满足这个东西就可以了:设p[i]为a[x]=i的个数的后缀和,那么对于每个位置要满足p[i]<=n-i+1,也就说,具体那个人根本就不关心
转换完问题我就傻掉了
设f[i][j]表示枚举到第i个位置,后i个位置共有j个编号的方案数
那么整个代码其实有用的就只有这句话:
f[i][j]=∑f[i+1][j-k-cn(i,i)]*C[n-cn(1,n)-(j-k-cn(i,i)-cn(i+1,n))][k]
其中cn(x,y)表示这个区间有多少已经内定了,k枚举放在第i个位置的编号个数(没有内定),然后n-cn(1,n)是一共有多少个人可以放编号,j-k-cn(i,i)-cn(i+n)是上一个状态去掉内定的放了多少编号
我觉得不难啊怎么我就是写不出来呢
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int maxn=310; LL mod; LL C[maxn][maxn]; void initC(int n) { C[0][0]=1; for(int i=1;i<=n;i++) { C[i][0]=1; for(int j=1;j<=n;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } } LL f[maxn][maxn]; int cnt[maxn];int cn(int x,int y){return cnt[y]-cnt[x-1];} int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int T; scanf("%d",&T); while(T--)//多组数据 { int n,m,x,y; scanf("%d%d%lld",&n,&m,&mod); initC(n); memset(cnt,0,sizeof(cnt)); for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),cnt[y]++; for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1]; bool bk=true; memset(f,0,sizeof(f));f[n+1][0]=1; for(int i=n;i>=1;i--) { if(cn(i,n)>n-i+1){bk=false;break;} int L=n-i+1,li=min(L,n-cn(1,i-1)); for(int j=cn(i,n);j<=li;j++) for(int k=0;k+cn(i,i)<=j;k++) f[i][j]=(f[i][j]+f[i+1][j-k-cn(i,i)]*C[n-cn(1,n)-(j-k-cn(i,i)-cn(i+1,n))][k])%mod; } if(bk==false)printf("NO\n"); else printf("YES %lld\n",f[1][n]); } return 0; }
pain and happy in the cruel world.