Codeforces Round #416 (Div. 2)
A.Vladik and Courtesy
题目链接:http://codeforces.com/contest/811/problem/A
暴力
代码如下:
1 #include <iostream> 2 using namespace std; 3 typedef long long ll; 4 ll a,b; 5 int main(void){ 6 cin>>a>>b; 7 ll t=1; 8 while(1){ 9 if(t&1)a-=t; 10 else b-=t; 11 t++; 12 if(a<0||b<0)break; 13 } 14 if(a<0)cout<<"Vladik"; 15 else cout<<"Valera"; 16 }
B.Vladik and Complicated Book
题目链接:http://codeforces.com/contest/811/problem/B
题目大意:判断区间内第$k$大元素是否为$x$.
离线+树状数组/主席树模板/暴力
看到数据范围$10^4$,想成学校的老年机,想半天想出个$O(mlgm+mlgn)$的,结果被告知$O(nm)$暴力可过= =
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 typedef long long ll; 6 int n,m,a[10005],c[10005],f[10005]; 7 bool ans[10005]; 8 struct node{ 9 int l,r,x,id; 10 friend bool operator<(node qq,node pp){ 11 return a[qq.x]<a[pp.x]; 12 } 13 }q[10005]; 14 int lowbit(int x){ 15 return x&-x; 16 } 17 void add(int x){ 18 for(int i=x;i<=n;i+=lowbit(i)) 19 c[i]++; 20 } 21 int sum(int x){ 22 int ans=0; 23 for(int i=x;i>0;i-=lowbit(i)) 24 ans+=c[i]; 25 return ans; 26 } 27 int main(void){ 28 scanf("%d%d",&n,&m); 29 for(int i=1;i<=n;++i){ 30 scanf("%d",&a[i]); 31 f[a[i]]=i; 32 } 33 for(int i=0;i<m;++i){ 34 scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].x); 35 q[i].id=i; 36 } 37 sort(q,q+m); 38 int l=1; 39 for(int i=0;i<m;++i){ 40 int id=q[i].id; 41 while(l<a[q[i].x]){ 42 add(f[l]); 43 l++; 44 } 45 int t=sum(q[i].r)-sum(q[i].l-1); 46 if(q[i].l+t==q[i].x)ans[id]=1; 47 else ans[id]=0; 48 } 49 for(int i=0;i<m;++i){ 50 if(ans[i])printf("Yes\n"); 51 else printf("No\n"); 52 } 53 }
C.Vladik and Memorable Trip
题目链接:http://codeforces.com/contest/811/problem/C
题目大意:将一个数组划分成若干个不交叉的块,要求块外不含块内元素,每个块的价值为块内不同元素的异或值,总价值为所有块的价值和,问最大价值。
DP
想着先区间合并再dp,然后算法错误...
看到数据范围其实$O(n^2)$可过,直接dp就好了...
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 typedef long long ll; 7 int n,a[5005],dp[5005],r[5005]; 8 bool vis[5005],f[5005]; 9 int main(void){ 10 scanf("%d",&n); 11 for(int i=0;i<n;++i){ 12 scanf("%d",&a[i]); 13 r[a[i]]=i; 14 } 15 int ans=0; 16 for(int i=0;i<n;++i){ 17 int t=a[i]; 18 if(!vis[t]){ 19 vis[t]=1; 20 memset(f,0,sizeof(f)); 21 f[t]=1; 22 bool flag=1; 23 int temp=t; 24 int R=r[t]; 25 for(int j=i+1;j<R;++j){ 26 if(vis[a[j]]&&a[j]!=t){ 27 flag=0;break; 28 } 29 if(!f[a[j]]){ 30 temp^=a[j]; 31 R=max(R,r[a[j]]); 32 f[a[j]]=1; 33 } 34 } 35 if(flag)dp[R]=max(dp[R],temp+ans); 36 } 37 ans=max(ans,dp[i]); 38 } 39 printf("%d\n",ans); 40 }
D.Vladik and Favorite Game
题目链接:http://codeforces.com/contest/811/problem/D
题目大意:交互题,要求输出从起点到终点的行动方向,其中左右和上下的方向可能会被改变一次。
XJB模拟
先dfs出不改变方向的情况下的原行动方向,然后一步一步模拟即可。
代码如下:
1 #include <cstdio> 2 using namespace std; 3 int n,m,d=-1,x,y,px,py,nx,ny; 4 char mp[105][105]; 5 char ans[10005]; 6 bool vis[105][105]; 7 void dfs(int px,int py,int k){ 8 if(mp[px][py]=='F'){ 9 d=k; 10 return; 11 } 12 int heng=0,shu=0; 13 if(px-1>=0&&mp[px-1][py]!='*')shu++; 14 if(px+1<n&&mp[px+1][py]!='*')shu++; 15 if(py-1>=0&&mp[px][py-1]!='*')heng++; 16 if(py+1<m&&mp[px][py+1]!='*')heng++; 17 if(shu>heng){ 18 if(px-1>=0&&!vis[px-1][py]&&mp[px-1][py]!='*'){ 19 vis[px-1][py]=1; 20 ans[k]='U'; 21 dfs(px-1,py,k+1); 22 if(d!=-1)return; 23 } 24 if(px+1<n&&!vis[px+1][py]&&mp[px+1][py]!='*'){ 25 vis[px+1][py]=1; 26 ans[k]='D'; 27 dfs(px+1,py,k+1); 28 if(d!=-1)return; 29 } 30 if(py-1>=0&&!vis[px][py-1]&&mp[px][py-1]!='*'){ 31 vis[px][py-1]=1; 32 ans[k]='L'; 33 dfs(px,py-1,k+1); 34 if(d!=-1)return; 35 } 36 if(py+1<m&&!vis[px][py+1]&&mp[px][py+1]!='*'){ 37 vis[px][py+1]=1; 38 ans[k]='R'; 39 dfs(px,py+1,k+1); 40 if(d!=-1)return; 41 } 42 }else{ 43 if(py-1>=0&&!vis[px][py-1]&&mp[px][py-1]!='*'){ 44 vis[px][py-1]=1; 45 ans[k]='L'; 46 dfs(px,py-1,k+1); 47 if(d!=-1)return; 48 } 49 if(py+1<m&&!vis[px][py+1]&&mp[px][py+1]!='*'){ 50 vis[px][py+1]=1; 51 ans[k]='R'; 52 dfs(px,py+1,k+1); 53 if(d!=-1)return; 54 } 55 if(px-1>=0&&!vis[px-1][py]&&mp[px-1][py]!='*'){ 56 vis[px-1][py]=1; 57 ans[k]='U'; 58 dfs(px-1,py,k+1); 59 if(d!=-1)return; 60 } 61 if(px+1<n&&!vis[px+1][py]&&mp[px+1][py]!='*'){ 62 vis[px+1][py]=1; 63 ans[k]='D'; 64 dfs(px+1,py,k+1); 65 if(d!=-1)return; 66 } 67 } 68 } 69 char another(char c){ 70 if(c=='R')return 'L'; 71 if(c=='L')return 'R'; 72 if(c=='U')return 'D'; 73 if(c=='D')return 'U'; 74 } 75 int main(void){ 76 scanf("%d%d",&n,&m); 77 for(int i=0;i<n;++i)scanf("%s",mp[i]); 78 dfs(0,0,0); 79 px=nx=0,py=ny=0; 80 bool heng=1,shu=1; 81 for(int i=0;i<d;){ 82 bool LR=ans[i]=='L'||ans[i]=='R'; 83 bool UD=ans[i]=='U'||ans[i]=='D'; 84 if(LR){ 85 if(heng)printf("%c\n",ans[i]); 86 else printf("%c\n",another(ans[i])); 87 if(ans[i]=='R')ny=py+1; 88 else ny=py-1; 89 }else{ 90 if(shu)printf("%c\n",ans[i]); 91 else printf("%c\n",another(ans[i])); 92 if(ans[i]=='D')nx=px+1; 93 else nx=px-1; 94 } 95 fflush(stdout); 96 scanf("%d%d",&x,&y);x--,y--; 97 if((x==-2&&y==-2)||mp[x][y]=='F')return 0; 98 if(x==px&&y==py){ 99 if(ans[i]=='L'&&y+1>=m)heng=0; 100 if(ans[i]=='R'&&y-1<0)heng=0; 101 if(ans[i]=='D'&&x-1<0)shu=0; 102 if(ans[i]=='U'&&x+1>=n)shu=0; 103 }else if(x!=nx||y!=ny){ 104 if(ans[i]=='R'&&y!=ny){ 105 heng=0; 106 printf("L\n"); 107 fflush(stdout); 108 scanf("%d%d",&x,&y);x--,y--; 109 } 110 if(ans[i]=='L'&&y!=ny){ 111 heng=0; 112 printf("R\n"); 113 fflush(stdout); 114 scanf("%d%d",&x,&y);x--,y--; 115 } 116 if(ans[i]=='U'&&y!=ny){ 117 shu=0; 118 printf("D\n"); 119 fflush(stdout); 120 scanf("%d%d",&x,&y);x--,y--; 121 } 122 if(ans[i]=='D'&&y!=ny){ 123 shu=0; 124 printf("U\n"); 125 fflush(stdout); 126 scanf("%d%d",&x,&y);x--,y--; 127 } 128 }else i++; 129 nx=px=x,ny=py=y; 130 } 131 }
E.Vladik and Entertaining Flags
题目链接:http://codeforces.com/contest/811/problem/E
题目大意:有一个$n \times m$的矩阵,$q$次询问,每次询问$l$列到$r$列围成的图中有多少连通块。
线段树+并查集
注意到$n \leqslant 10$,故可以用线段树维护。
每段维护从$L$列到$R$列的图中有多少连通块,以及$L$列及$R$列的段内数字编号(保证段内的联通的数字编号是相同的)。
段与段合并时,只需判断相邻的数字是否相同,若相同且不为同一连通块,则合并。
查询前,需要将段两端的数字编号的$pre$指向自己(build操作中有可能将$pre$指向了其他段中的数字编号;同时因为段内联通的数字编号相同,故该操作不会改变段内数字的连通性),之后同合并操作。
复杂度$O(nmlgm+qnlgm)$.
代码如下:
1 #include <cstdio> 2 #define lson x<<1,l,mid 3 #define rson x<<1|1,mid+1,r 4 using namespace std; 5 int n,m,q,mp[12][100005],pre[1000005],tot; 6 bool flag; 7 struct node{ 8 int num; 9 int L[12],R[12]; 10 }a[100005<<2],ans; 11 void init(int x){ 12 for(int i=0;i<=x;++i)pre[i]=i; 13 } 14 int Find(int x){ 15 return pre[x]==x?x:pre[x]=Find(pre[x]); 16 } 17 void Union(int a,int b){ 18 int x=Find(a),y=Find(b); 19 if(x!=y)pre[x]=y; 20 } 21 void push_up(int x,int mid){ 22 int l=x<<1,r=x<<1|1; 23 a[x].num=a[l].num+a[r].num; 24 for(int i=0;i<n;++i){ 25 if(mp[i][mid]==mp[i][mid+1]){ 26 if(Find(a[l].R[i])!=Find(a[r].L[i])){ 27 a[x].num--; 28 Union(a[l].R[i],a[r].L[i]); 29 } 30 } 31 } 32 for(int i=0;i<n;++i){ 33 a[x].L[i]=Find(a[l].L[i]); 34 a[x].R[i]=Find(a[r].R[i]); 35 } 36 } 37 void build(int x,int l,int r){ 38 if(l==r){ 39 a[x].num=1; 40 a[x].L[0]=a[x].R[0]=++tot; 41 for(int i=1;i<n;++i){ 42 if(mp[i][r]==mp[i-1][r]){ 43 a[x].L[i]=a[x].R[i]=tot; 44 }else{ 45 tot++,a[x].num++; 46 a[x].L[i]=a[x].R[i]=tot; 47 } 48 } 49 return; 50 } 51 int mid=(l+r)>>1; 52 build(lson); 53 build(rson); 54 push_up(x,mid); 55 } 56 void query(int x,int l,int r,int ql,int qr){ 57 if(ql<=l&&r<=qr){ 58 if(flag){ 59 flag=0; 60 ans=a[x]; 61 return; 62 }else{ 63 ans.num+=a[x].num; 64 for(int i=0;i<n;++i){ 65 pre[ans.R[i]]=ans.R[i]; 66 pre[a[x].L[i]]=a[x].L[i]; 67 pre[a[x].R[i]]=a[x].R[i]; 68 } 69 for(int i=0;i<n;++i){ 70 if(mp[i][l-1]==mp[i][l]){ 71 if(Find(ans.R[i])!=Find(a[x].L[i])){ 72 ans.num--; 73 Union(ans.R[i],a[x].L[i]); 74 } 75 } 76 } 77 for(int i=0;i<n;++i) 78 ans.R[i]=Find(a[x].R[i]); 79 return; 80 } 81 } 82 int mid=(l+r)>>1; 83 if(ql<=mid)query(lson,ql,qr); 84 if(mid<qr)query(rson,ql,qr); 85 } 86 int main(void){ 87 scanf("%d%d%d",&n,&m,&q); 88 init(n*m); 89 for(int i=0;i<n;++i) 90 for(int j=0;j<m;++j) 91 scanf("%d",&mp[i][j]); 92 build(1,0,m-1); 93 while(q--){ 94 int x,y; 95 scanf("%d%d",&x,&y);x--;y--; 96 flag=1; 97 query(1,0,m-1,x,y); 98 printf("%d\n",ans.num); 99 } 100 }