省队集训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!枚举所有排列,对于每种排列在没有多余空隙的情况下占的空隙数为
对于每种排列我们求出占的空隙数x,那么剩下的l-1-x个空隙可以任意插入到n+1个空隙中。
实际上就是插板发l-1-x个空隙,分到n+1个区间中,允许区间为空,答案为
算法的瓶颈在于n!的排列。
如果我们能求出空隙数为x的排列数,那么就可以直接利用组合数计算了。
考虑从小到大依次插入
因为是从小到大插入的,所以如果插入到两个已经插入的数之间且不再从这个数两侧插入数,那么对于空隙的贡献就是插入的数*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);}
- 省队集训DAY4
- 省队集训Round3 DAY4
- 福建省队集训被虐记——DAY4
- 湖南集训Day4
- ACM集训day4
- 沈阳集训day4
- 雅礼集训Day4
- 北京集训DAY4
- 湖南集训 Day4
- 2015湖南省队集训DAY4——hoodle
- 【集训Day4 动态规划】蛙人
- 陕西省集训 day4(搜索下)
- 【泉州一中国庆集训day4】破解
- 【泉州一中国庆集训day4】书稿
- 2017暑假七林集训day4
- 南师附中集训总结Day4
- 2017 暑假艾教集训 day4
- 【集训Day4 动态规划】轮船问题
- codeforces——189A——Cut Ribbon
- css基础(未完待续)
- phpstorm技巧随笔
- Ubuntu16.04 + cuda8.0 + GTX1080 + matlab14.04a + Opencv3.0 + caffe 安装教程
- 欢迎使用CSDN-markdown编辑器
- 省队集训DAY4
- CAS单点登录报错 org.jasig.cas.client.util.XmlUtils 必须由匹配的结束标记
- 好看的人太多,有趣的人太少
- windows中使用make
- bzoj4916 神犇和蒟蒻
- Redis持久化:aof和rdb
- 中国剩余定理题——Biorhythms
- Android 之 控件的背景样式总结
- 正则案例