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 }
View Code

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 }
View Code

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;
}
View Code

 

posted @ 2019-02-15 17:32  蒟蒻LQL  阅读(208)  评论(0编辑  收藏  举报