zoukankan      html  css  js  c++  java
  • Codeforces 79D

    Codeforces 题目传送门 & 洛谷题目传送门

    一个远古场的 *2800,在现在看来大概 *2600 左右罢(

    不过我写这篇题解的原因大概是因为这题教会了我一个套路罢(

    首先注意到每次翻转的是一个区间,那么我们可以考虑它的差分序列(这就是这题教会我的套路,碰到区间操作有关的问题不妨考虑它的差分序列,这样可将影响多个元素的区间操作转化为 (2) 个单点操作,其实第一次碰见这个套路是在这个题,可是由于当时比较 naive 没能及时补题并整理这些方法),那么每次操作显然是将差分序列上两个单点进行翻转,而翻转的两个单点的距离恰好等于操作区间的长度,于是现在问题转化为:你有一个初始为 (0) 的长度为 (n+1) 的序列 (a),你可以选择两个单点并将它们的状态翻转,满足这两个单点的距离属于某个集合 (S),要求最少多少次操作将序列变为给定序列 (a')

    考虑怎样解决转化后的问题,首先取两个单点并翻转显然是不影响 (1) 的个数的奇偶性的,因此如果 (1) 的个数是奇数那直接 (-1) 即可,否则显然我们会将这些 (1) 两两配对,对于同一对中的两个位置 (x,y),我们会以如下方式让 (x,y) 上的数都变为 (0)

    重复以下步骤若干次:

    • 选择一个长度 (lin S) 并将 (x)(x+l)(x-l) 同时翻转,这样即可将 (x) 上的 (1) 转移到 (x+l)(x-l) 上。

    直到 (x+l=ylor x-l=y)

    我们记 (d_{x,y}) 为将 (x,y) 上的数变为 (0) 的最小步骤,那么显然我们可以以每个 (1) 为起点进行一遍 BFS 求出所有 (d_{x,y})。注意到此题 (kle 10),因此差分序列上 (1) 的个数 (le 20),故考虑状压 (dp)(dp_S) 表示将 (S) 中的 (1) 变为 (0) 的最小代价,转移就枚举两个 (x,y otin S) 然后 (dp_{Scup{x}cup{y}}leftarrow dp_S+d_{x,y}) 即可。

    据说有更优秀的二分图最小权完美匹配的做法?i 了 i 了,可惜我懒癌爆发懒得写了

    复杂度 (2^kk^2+nmk),其中 (k=20)

    const int MAXN=1e4;
    const int MAXM=100;
    const int MAXK=20;
    const int MAXP=1<<20;
    const int INF=0x3f3f3f3f;
    int n,k,m,a[MAXN+5],b[MAXN+5],l[MAXM+5],id[MAXN+5],pos[MAXK+5];
    int dis[MAXN+5],d[MAXK+5][MAXK+5],cnt;
    void bfs(int s){
    	queue<int> q;memset(dis,-1,sizeof(dis));
    	dis[s]=d[id[s]][id[s]]=0;q.push(s);
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		if(id[x]) d[id[s]][id[x]]=dis[x];
    		for(int i=1;i<=m;i++){
    			if(x+l[i]<=n+1){
    				if(!~dis[x+l[i]]) dis[x+l[i]]=dis[x]+1,q.push(x+l[i]);
    			} if(x-l[i]>=1){
    				if(!~dis[x-l[i]]) dis[x-l[i]]=dis[x]+1,q.push(x-l[i]);
    			}
    		}
    	}
    }
    int dp[MAXP+5];
    int main(){
    	scanf("%d%d%d",&n,&k,&m);
    	for(int i=1,x;i<=k;i++) scanf("%d",&x),a[x]=1;
    	for(int i=1;i<=m;i++) scanf("%d",&l[i]);
    	for(int i=1;i<=n+1;i++) b[i]=a[i]^a[i-1];
    	for(int i=1;i<=n+1;i++) if(b[i]) id[i]=++cnt,pos[cnt]=i;
    	memset(d,63,sizeof(d));
    	for(int i=1;i<=n+1;i++) if(id[i]) bfs(i);
    	memset(dp,63,sizeof(dp));dp[0]=0;
    //	printf("%d
    ",cnt);
    //	for(int i=1;i<=cnt;i++) for(int j=1;j<=cnt;j++)
    //		printf("%d%c",d[i][j]," 
    "[j==cnt]);
    	for(int i=0;i<(1<<cnt);i++){
    		if(dp[i]>=INF) continue;
    		for(int j=1;j<=cnt;j++) for(int l=1;l<j;l++)
    			if((~i>>j-1&1)&&(~i>>l-1&1))
    				chkmin(dp[i|(1<<j-1)|(1<<l-1)],dp[i]+d[j][l]);
    	} printf("%d
    ",(dp[(1<<cnt)-1]>=INF)?-1:dp[(1<<cnt)-1]);
    	return 0;
    }
    
  • 相关阅读:
    01矩阵扩展
    蒙特卡罗仿真
    某幂相关数学结论
    分式乘法变加减
    ICPC模板排版工具
    windows下mysql使用实录
    随机题目小结
    工作用linux命令汇总
    小数化分数的O(log2n)解法
    博弈总结
  • 原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-79D.html
Copyright © 2011-2022 走看看