Codeforces Round #532 (Div. 2)
D. Dasha and Chess
交互题。题意: 有666个黑色的车和1个白色的国王在999*999的棋盘上,当白色的国王和任意一个黑色的车处于同一行,同一列的时候,白色国王胜利。白色的国王开始先行棋,NN执白,每一步可以横着,竖着或者斜着走一格,如果目标格子上已经有棋了那么就不能走。黑色的车每一次可以从一个格子走到任意的其他格子。每个玩家走2000步,如果白棋没法将军则黑棋胜利。现在你来执白棋,并想办法取得胜利。
题解:
我们先把白棋走到棋盘的中间,然后一直向黑棋最多的角斜着走,这样一定可以将军。正确性显然。
1 #include<stdio.h> 2 #include<stdlib.h> 3 const int maxn = 1005; 4 int x,y,u,v,w,px[maxn],py[maxn],vis[maxn][maxn],sum[5],sum2[5]; 5 int dis[5][2]={0,0,-1,-1,-1,1,1,-1,1,1}; 6 void move_(int xx,int yy) 7 { 8 x+=xx; 9 y+=yy; 10 if(vis[x][y]) x-=xx; 11 printf("%d %d\n",x,y); 12 fflush(stdout); 13 scanf("%d%d%d",&u,&v,&w); 14 if(u==-1&&v==-1&&w==-1) exit(0); 15 vis[px[u]][py[u]]=0; 16 vis[v][w]=1; 17 px[u]=v; 18 py[u]=w; 19 return ; 20 } 21 int main() 22 { 23 scanf("%d%d",&x,&y); 24 for(int i=1;i<=666;i++) 25 { 26 scanf("%d%d",&px[i],&py[i]); 27 vis[px[i]][py[i]]=1; 28 } 29 int dir=-1; 30 while(x<500) move_(1,0); 31 while(y<500) move_(0,1); 32 while(x>500) move_(-1,0); 33 while(y>500) move_(0,-1); 34 for(int i=1;i<=499;i++) for(int j=1;j<=499;j++) if(vis[i][j]) sum[1]++; 35 for(int i=1;i<=499;i++) for(int j=501;j<=999;j++) if(vis[i][j]) sum[2]++; 36 for(int i=501;i<=999;i++) for(int j=1;j<=499;j++) if(vis[i][j]) sum[3]++; 37 for(int i=501;i<=999;i++) for(int j=501;j<=999;j++) if(vis[i][j]) sum[4]++; 38 sum2[1]=sum[1]+sum[2]+sum[3]; 39 sum2[2]=sum[2]+sum[1]+sum[4]; 40 sum2[3]=sum[3]+sum[1]+sum[4]; 41 sum2[4]=sum[4]+sum[2]+sum[3]; 42 int maxx=0; 43 for(int i=1;i<=4;i++) 44 { 45 if(sum2[i]>maxx) 46 { 47 maxx=sum2[i]; 48 dir=i; 49 } 50 } 51 while(true) move_(dis[dir][0],dis[dir][1]); 52 return 0; 53 }
E. Andrew and Taxi
题意:
给你一个有边权的有向图,反转一条边的代价是这条边的边权,反转多个边的代价是所有反转边里面边权最大的那条边的边权,问让这个图不存在环的最小代价,以及被反转的边的编号。
题解:
我们先这么想,给出一个最小代价Min,我们如何判断是否有环?因为代价小于等于Min的边都可以选择反转,所以只要对于边权大于Min的边跑拓扑排序,如果没环就说明Min是可行的。那么反转哪些边呢?我们现在已经得到了拓扑序,然后只要对于每一条wi<=Min的从u到v的边,如果topo[v]<topo[u],那么这条边就需要被反转。显然Min是可以通过二分获得的。
1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 #include <queue> 6 #include <vector> 7 8 using namespace std; 9 const int maxn=100000+100; 10 int n,m,sz; 11 int head[maxn],Next[maxn],to[maxn],w[maxn],num[maxn]; 12 void init(){ 13 sz=0; 14 memset(head,-1,sizeof(head)); 15 } 16 void add_edge(int a,int b,int c,int cnt){ 17 ++sz; 18 to[sz]=b;w[sz]=c;num[sz]=cnt;Next[sz]=head[a];head[a]=sz; 19 } 20 int topo[maxn],Topo[maxn],vis[maxn],cnt; 21 22 bool is_circle(int u,int mid){ 23 vis[u]=-1; 24 for(int i=head[u];i!=-1;i=Next[i]){ 25 int v=to[i]; 26 if(w[i]<=mid)continue; 27 if(vis[v]==-1)return true; 28 if(!vis[v]) 29 if(is_circle(v,mid)) 30 return true; 31 } 32 topo[cnt]=u; 33 Topo[u]=cnt--; 34 vis[u]=1; 35 return false; 36 } 37 int Max; 38 int main(){ 39 init(); 40 scanf("%d%d",&n,&m); 41 for(int i=1;i<=m;i++){ 42 int a,b,c; 43 scanf("%d%d%d",&a,&b,&c); 44 Max=max(Max,c); 45 add_edge(a,b,c,i); 46 } 47 cnt=n; 48 // for(int i=1;i<=n;i++){ 49 // printf("%d ",topo[i]); 50 // } 51 int l=0,r=Max,ans=0; 52 while(l<=r){ 53 int mid=l+(r-l)/2; 54 memset(vis,0,sizeof(vis)); 55 int flag=0; 56 cnt=n; 57 for(int i=1;i<=n;i++){ 58 if(!vis[i]){ 59 if(is_circle(i,mid)){ 60 flag=1; 61 break; 62 } 63 } 64 } 65 66 if(!flag){ 67 ans=mid; 68 r=mid-1; 69 }else{ 70 l=mid+1; 71 } 72 } 73 //printf("%d\n",ans); 74 vector<int>ANS; 75 memset(vis,0,sizeof(vis)); 76 cnt=n; 77 for(int i=1;i<=n;i++) 78 if(!vis[i]) 79 is_circle(i,ans); 80 81 for(int i=1;i<=n;i++){ 82 for(int j=head[i];j!=-1;j=Next[j]){ 83 int v=to[j]; 84 // printf("%d %d %d %d %d\n",i,v,w[j],Topo[i],Topo[v]); 85 if(w[j]<=ans&&Topo[v]<Topo[i]){ 86 ANS.push_back(num[j]); 87 } 88 } 89 } 90 printf("%d %d\n",ans,ANS.size()); 91 for(int i=0;i<ANS.size();i++){ 92 printf("%d ",ANS[i]); 93 } 94 return 0; 95 }
F.题意:
给出n个数字ci和q个询问,每次询问给出li和ri,需要你求出从区间[li,ri]内选出一些数字,异或值最大。
题解:
这个题一看就是线性基。我们先来看一下简化版,也是线性基最经典的应用之一。如果是只有一次询问,问从这n个数字中选出一些数使得异或值最大。该怎么做?我们先求出这n个数字的线性基,然后从高位开始异或如果异或上这个值数变大那么就异或上。想想为什么?因为异或上高位的可能会影响异或低位的,但是结果一定不会更差。我们回到这个题,我们考虑离线的做法,将询问按照右端点排序,然后从1到n扫开始构造线性基,但是和正常构造的区别是,这里构造的时候,对于当前的元素,它所在线性基的位置一定是越高越好的。那么我们这里就有一个“替换”操作。详细的可以看代码。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> using namespace std; typedef long long LL; const int maxn=500000+10; int n,q; int c[maxn],x[100],l[100]; struct Seg{ int l,r; }seg[maxn]; LL ans[maxn]; vector<int>en[maxn]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&c[i]); } scanf("%d",&q); for(int i=1;i<=q;i++){ scanf("%d%d",&seg[i].l,&seg[i].r); en[seg[i].r].push_back(i); } for(int i=1;i<=n;i++){ int pos=i; for(int j=62;j>=0;j--){ if(((c[pos]>>j)&1)==0)continue; if(!x[j]){x[j]=c[pos];l[j]=pos;break;} else if(l[j]<pos){ //swap(x[j],c[pos]); x[j]=c[pos]; swap(pos,l[j]); } c[pos]^=x[j]; } for(int j=0;j<en[i].size();j++){ ans[en[i][j]]=0; for(int k=62;k>=0;k--){ if(l[k]>=seg[en[i][j]].l){ ans[en[i][j]]=max(ans[en[i][j]],x[k]^ans[en[i][j]]); } } } } for(int i=1;i<=q;i++){ printf("%I64d\n",ans[i]); } return 0; }