星空[好题][题意转化]
Posted wang-hesong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了星空[好题][题意转化]相关的知识,希望对你有一定的参考价值。
题目大意:给你一个0/1串,让你对区间亦或(有特定几个长度限制操作),最后让你求出把这个0/1串变成全0串的最小操作数;
前置问题:
首先发现对原序列区间整体亦或很不好控制,因为会不断出现新的1;
那我们怎么办?
想想差分;
与普通线性数组差分一样,若原序列有初始值,则需要把原序列转化为差分序列;
具体求法即为:b[i]=a[i]-a[i-1];(b数组为差分数组,a为原数组)
同理亦或差分:b[i]=a[i]^a[i-1];
于是我们可以把原序列转化成为差分序列;
那我们为什么要把元序列转化为差分序列呢?
1.考虑区间修改,元序列有0与1相互转化,并会蹦出新的1;
2.而差分数组只对两端进行修改;只有0/1的单次转化;
3.最重要的性质,差分数组与原数组的最终状态一直,都是全0串;(若 原序列为000000000 ,亦或差分序列不也得是00000000吗)
一-------于是成功转化题意;
在原序列的差分序列上进行如下操作:
1.把可把1在序列上向左移动或向右移动几种固定长度,当1与1想碰时变为0;
2.求最终把这个序列变为全0串的最小操作数;
eg.差分序列:000110001100 区间修改就是选区间的左右端点(不严谨),然后对两个端点亦或,这不就是把左端点1亦或成0,而右端点0亦或成1吗;其实就是把1移动......
那这样就很好做了;
既然有固定的长度;那就把长度看成边;
把差分序列上的每个点都和他能一次操作到达的点连边;而边权就是1;一部操作吗.......
二-------于是就形成了一个图;
而我们要知道的就是1之间互相到达的代价;在图中转化为距离;
那就跑dijistrla呗;只不过得跑2k次;
但没关系啊;NlogN*2k完全可以;
一波dijistrla后,我们求出了dis[i][j] 代表的是差分序列上 第i个1 到 第j个1的 代价;
三-------于是问题又转化了:
你有2k个物品,两两能以一定代价互相匹配,求把这2k个物品分为k对的最小代价;
而k<=8;2k<=16;
状压呗;O((k^2)*(2^2k))可以卡过;
最终状态就是(1<<2k)-1;
附上自带常数的代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define rd(x) scanf("%lld",&x) 4 #define int long long 5 int a[400050],b[400050],edge[1000],pos[100],id[400050]; 6 int N,K,M; 7 int dis[100][100]; 8 int f[1<<20|1],g[100][100]; 9 vector<int>to[400500]; 10 11 struct node 12 int to,dis; 13 node(int a,int b)to=a;dis=b; 14 friend bool operator<(node a,node b) 15 return a.dis>b.dis; 16 17 ; 18 19 bool v[40050]; 20 int d[40050]; 21 void diji(int st) 22 priority_queue<node>q; 23 memset(d,0x3f,sizeof(d));memset(v,0,sizeof(v)); 24 d[st]=0;q.push(node(st,d[st])); 25 register int u,w; 26 while(q.size()) 27 u=q.top().to;w=q.top().dis;q.pop(); 28 if(v[u])continue; 29 v[u]=1; 30 for(register int i=0,y;i<to[u].size();++i) 31 y=to[u][i]; 32 if(v[y])continue; 33 if(d[u]+1<d[y]) 34 d[y]=d[u]+1; 35 q.push(node(y,d[y])); 36 37 38 39 for(int i=1;i<=pos[0];++i) 40 if(id[i]!=st) 41 dis[id[st]][i]=dis[i][id[st]]=d[pos[i]]; 42 43 44 45 signed main() 46 rd(N);rd(K);rd(M); 47 for(register int i=1,p;i<=K;++i) 48 rd(p);a[p]=1; 49 50 for(register int i=1;i<=M;++i) 51 rd(edge[i]); 52 53 for(register int i=1;i<=N;++i) 54 for(register int j=1;j<=M;++j) 55 if(i+edge[j]>N+1)break; 56 to[i].push_back(i+edge[j]); 57 to[i+edge[j]].push_back(i); 58 59 60 for(register int i=1;i<=N+1;++i) 61 b[i]=a[i]^a[i-1]; 62 if(b[i])pos[++pos[0]]=i,id[i]=pos[0]; 63 64 for(register int i=1;i<=pos[0]-1;++i)diji(pos[i]); 65 /*for(int i=1;i<=pos[0];++i) 66 for(int j=1;j<=pos[0];++j)cout<<dis[i][j]<<" "; 67 cout<<endl; 68 */ 69 for(register int i=1;i<=pos[0];++i) 70 for(register int j=1;j<=pos[0];++j) 71 if(i!=j)g[i][j]=(1<<i-1)|(1<<j-1); 72 memset(f,0x3f,sizeof(f));register int Z=(1<<pos[0])-1; 73 f[0]=0; 74 for(register int i=0;i<=Z;++i) 75 76 if(i==Z)break; 77 for(register int j=1;j<=pos[0];++j) 78 if((i&(1<<j-1))==0) 79 for(register int k=j+1;k<=pos[0];++k) 80 if((i&(1<<k-1))==0) 81 f[i|g[j][k]]=min(f[i|g[j][k]],f[i]+dis[j][k]); 82 83 84 85 86 printf("%lld\n",f[Z]); 87
以上是关于星空[好题][题意转化]的主要内容,如果未能解决你的问题,请参考以下文章