省队集训DAY4

来源:互联网 发布:网络平台合作协议 编辑:程序博客网 时间:2024/05/16 06:20

T1

这里写图片描述
这里写图片描述

题解

将行与列分开考虑,每两个#之间属于一个连通块。
对于每个连通块建立节点,如果只从连通块中选取一个点,那么不会产生相互攻击的棋子。选取第2个点的时候会产生1的贡献,选取第三个点的时候会产生2的贡献。。。。
行列都是如此,那么S->行所代表的连通块,列代表的连通块->T。对于每个连通块连size条边,每条边的容量为1,费用依次递增。
对于每个不是#的位置,一定属于两个连通块,在两个连通块之间连容量为1,费用为0的边,保证每个点只会被选择一次。

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#include<queue>#define N 500003#define inf 1000000000using namespace std;int tot,point[N],v[N],nxt[N],remain[N],c[N],dis[N],can[N],last[N];int belong[53][53],belong1[53][53],mp[53][53],size[N];int n,m,ans,cnt,S,T,TT,ck[N];void add(int x,int y,int z,int k){    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; c[tot]=k;    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; c[tot]=-k;    //cout<<x<<" "<<y<<" "<<z<<" "<<k<<endl;}int addflow(int s,int t){    int ans=inf; int now=t;    while (now!=s) {        ans=min(ans,remain[last[now]]);        now=v[last[now]^1];    }    now=t;    while (now!=s) {        remain[last[now]]-=ans;        remain[last[now]^1]+=ans;        now=v[last[now]^1];    }    return ans;}int spfa(int s,int t){    for (int i=1;i<=t;i++) dis[i]=inf,can[i]=0;    dis[s]=0; queue<int> p; p.push(s); can[s]=1;    while (!p.empty()){        int now=p.front(); p.pop();        can[now]=0;        for (int i=point[now];i!=-1;i=nxt[i])         if (remain[i]&&dis[v[i]]>dis[now]+c[i]){            dis[v[i]]=dis[now]+c[i];            last[v[i]]=i;            if (!can[v[i]]) {                can[v[i]]=1;                p.push(v[i]);             }         }    }    int flow=addflow(s,t);    ans+=dis[t];}int main(){    freopen("chess.in","r",stdin);    freopen("chess.out","w",stdout);    scanf("%d",&n); int sum=0;    tot=-1;    memset(point,-1,sizeof(point));    for (int i=1;i<=n;i++) {        char s[53]; scanf("%s",s+1);        for (int j=1;j<=n;j++)         if (s[j]=='#') mp[i][j]=1;         else sum++;    }    for (int i=1;i<=n;i++)        for (int j=1;j<=n;j++){         if (mp[i][j]) continue;         if (j==1||mp[i][j-1]) ++cnt;         belong[i][j]=cnt;         size[cnt]++;        }    int k=cnt;    for (int i=1;i<=n;i++)       for (int j=1;j<=n;j++){        if (mp[j][i]) continue;        if (j==1||mp[j-1][i]) ++cnt;        belong1[j][i]=cnt;        size[cnt]++;       }    S=cnt+1; T=S+1; TT=T+1;    for (int i=1;i<=k;i++) {        add(S,i,1,0);        for (int j=1;j<size[i];j++) add(S,i,1,j);    }    for (int i=k+1;i<=cnt;i++) {        add(i,T,1,0);        for (int j=1;j<size[i];j++) add(i,T,1,j);    }    for (int i=1;i<=n;i++)     for (int j=1;j<=n;j++)      if (!mp[i][j]) add(belong[i][j],belong1[i][j],1,0);    for (int i=1;i<=sum;i++){        add(T,TT,1,0);        spfa(S,TT);        ck[i]=ans;    }    scanf("%d",&m);    for (int i=1;i<=m;i++){        int x; scanf("%d",&x);        printf("%d\n",ck[x]);    }}

T2

这里写图片描述

题解

考虑最暴力的做法,n!枚举所有排列,对于每种排列在没有多余空隙的情况下占的空隙数为n1i=1max(ai,ai+1)
对于每种排列我们求出占的空隙数x,那么剩下的l-1-x个空隙可以任意插入到n+1个空隙中。
实际上就是插板发l-1-x个空隙,分到n+1个区间中,允许区间为空,答案为C(lx+n1,n)
算法的瓶颈在于n!的排列。
如果我们能求出空隙数为x的排列数,那么就可以直接利用组合数计算了。
考虑从小到大依次插入
f[i][j][k]表示插入到第i个数,j个能插入的位置,占的空隙数为k的方案数。注意能放数的位置不包括两边,所以初始值是dp[1][0][0]=1
因为是从小到大插入的,所以如果插入到两个已经插入的数之间且不再从这个数两侧插入数,那么对于空隙的贡献就是插入的数*2,能插入的位置-1
如果插入一个位置,只保证插入位置的一侧不能再插入,那么对于空隙的贡献就是插入的数,能插入的位置不变。
如果插入一个位置,两侧都可以再插入数(再插入的数一定比当前数大),那么对于空隙的贡献就是0,能插入的位置+1.
两头的位置单独考虑一下。

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#define N 101#define LL long longusing namespace std;int n,l;int dp[N][N][N*N],p;int cnt,prime[N],c[N],tmp[N],pd[N],sum[N];void init(){    for (int i=2;i<=n;i++){        if (!pd[i]) prime[++cnt]=i;        for (int j=1;j<=cnt;j++){            if (prime[j]*i>n) break;            pd[prime[j]*i]=1;            if (i%prime[j]==0) break;        }    }}LL C(int K){    LL ans=1;    for (int i=1;i<=cnt;i++) tmp[i]=0;    for (int i=K-n+1;i<=K;i++){        int x=i;        for (int j=1;j<=cnt;j++)         while (tmp[j]<c[j]&&x%prime[j]==0)          x/=prime[j],tmp[j]++;        ans=(LL)ans*x%p;    }    return ans;}int main(){    freopen("tower.in","r",stdin);    freopen("tower.out","w",stdout);    scanf("%d%d%I64d",&n,&l,&p);    init();     for (int i=1;i<=n;i++) {        int x=i;        for(int j=1;j<=cnt;j++)            while (x%prime[j]==0) c[j]++,x/=prime[j];       }    for (int i=1;i<=n;i++) sum[i]=sum[i-1]+2*i;    dp[1][0][0]=1;    for (int i=1;i<n;i++) {        for (int j=1;j<=min(i-1,n-i);j++)         for (int k=0;k<=sum[i];k++){          dp[i+1][j-1][k+(i+1)*2]=(dp[i+1][j-1+0][k+(i+1)*2]+(LL)j*dp[i][j][k]%p)%p;          dp[i+1][j][k+(i+1)]=(dp[i+1][j][k+(i+1)]+(LL)2*j*dp[i][j][k]%p)%p;          dp[i+1][j+1][k]=(dp[i+1][j+1][k]+(LL)j*dp[i][j][k]%p)%p;        }        for (int j=0;j<=min(i-1,n-i);j++)         for (int k=0;k<=sum[i];k++){            dp[i+1][j][k+(i+1)]=(dp[i+1][j][k+(i+1)]+(LL)2*dp[i][j][k]%p)%p;            dp[i+1][j+1][k]=(dp[i+1][j+1][k]+(LL)2*dp[i][j][k]%p)%p;         }    }    LL ans=0;    for (int i=0;i<=sum[n];i++){        LL t=C(l-i+n-1);        ans=(ans+(LL)t*dp[n][0][i]%p)%p;    }    printf("%I64d\n",ans);}
原创粉丝点击