2015 Multi-University Training Contest 4
1001 Olympiad
签到题1。
1 # include <iostream> 2 # include <cstdio> 3 using namespace std; 4 int sum[100001]={0}; 5 6 bool judge(int x) 7 { 8 int cnt[10]={0}; 9 while(x>0) 10 { 11 cnt[x%10]++; 12 if(cnt[x%10]>1) return false; 13 x/=10; 14 } 15 return true; 16 } 17 18 int main(void) 19 { 20 for(int i=1;i<=100000;i++) 21 if(judge(i)) sum[i]=sum[i-1]+1; 22 else sum[i]=sum[i-1]; 23 int T; cin>>T; 24 while(T--) 25 { 26 int a,b; scanf("%d%d",&a,&b); 27 printf("%d\n",sum[b]-sum[a-1]); 28 } 29 return 0; 30 }
1002 Problem Killer
签到题2。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 using namespace std; 6 typedef long long LL; 7 # define maxn 1000010 8 LL a[maxn]; 9 10 int main(void) 11 { 12 int T; cin>>T; 13 while(T--) 14 { 15 int n; scanf("%d",&n); 16 for(int i=1;i<=n;i++) scanf("%I64d",a+i); 17 if(n==1) {printf("1\n"); continue;} 18 if(n==2) {printf("2\n"); continue;} 19 int maxAP=2,maxGP=2; 20 for(int i=3;i<=n;i++) 21 { 22 if(n-i+3<=maxAP) break; 23 if(a[i]+a[i-2]==a[i-1]+a[i-1]) 24 { 25 int tem=3,j; 26 for(j=i+1;j<=n;j++) 27 { 28 if(a[j]+a[j-2]==a[j-1]+a[j-1]) tem++; 29 else break; 30 } 31 maxAP=max(maxAP,tem); 32 i=j; 33 } 34 } 35 for(int i=3;i<=n;i++) 36 { 37 if(n-i+3<=maxGP||n-i+3<=maxAP) break; 38 if(a[i]*a[i-2]==a[i-1]*a[i-1]) 39 { 40 int tem=3,j; 41 for(j=i+1;j<=n;j++) 42 { 43 if(a[j]*a[j-2]==a[j-1]*a[j-1]) tem++; 44 else break; 45 } 46 maxGP=max(maxGP,tem); 47 i=j; 48 } 49 } 50 printf("%d\n",max(maxAP,maxGP)); 51 } 52 return 0; 53 }
1004 Route Statistics
1005 Simple Problem
1006 Test for Rikka
1007 Undirected Graph
1009 Walk Out
先广搜(向4个方向)连续0至离出口最近的位置。从而保证了二进制数位最小。
后面就只能走右下方向了。再以斜对角线的方式搜索。
如果某一斜对角线中能达到的都是1的话。考虑所有能达到的1的后继。
如果某一斜对角线存在能达到的0。就只考虑能达到的0的后继。
反思:
前面思路很简单。
其实后面也是一个广搜。但是如果拘泥于写队列的广搜形式。代码就会很复杂。
关键是要想到在方格图中广搜能以斜对角线搜索这种简便形式出现。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <queue> 6 using namespace std; 7 typedef pair<int,int> pii; 8 int n,m,G[1010][1010],vis[1010][1010]; 9 int step[][2]={{0,1},{1,0},{0,-1},{-1,0}}; 10 11 bool in(int x,int y) 12 { 13 return (x>0&&x<=n&&y>0&&y<=m); 14 } 15 16 int main(void) 17 { 18 int T; cin>>T; 19 while(T--) 20 { 21 scanf("%d%d",&n,&m); 22 for(int i=1;i<=n;i++) 23 { 24 char s[1010]; 25 scanf("%s",s+1); 26 for(int j=1;j<=m;j++) 27 G[i][j]=(s[j]=='1')?1:0; 28 } 29 memset(vis,0,sizeof(vis)); 30 int sum=1; 31 queue<pii> q; 32 q.push(pii(0,1)); 33 while(!q.empty()) 34 { 35 pii tem=q.front(); q.pop(); 36 int tx=tem.first,ty=tem.second; 37 for(int i=0;i<4;i++) 38 { 39 int x=tx+step[i][0],y=ty+step[i][1]; 40 if(in(x,y)&&!vis[x][y]) 41 { 42 if(!G[x][y]) 43 { 44 q.push(pii(x,y)); 45 sum=max(sum,x+y); 46 } 47 vis[x][y]=1; 48 } 49 } 50 } 51 if(sum>=m+n-1) {printf("%d\n",G[n][m]); continue;} 52 int now=1; 53 for(int i=sum+1;i<=m+n;i++) 54 { 55 int next=1; 56 for(int j=1;j<i;j++) 57 { 58 int x=j,y=i-j; 59 if(!in(x,y)||!vis[x][y]) continue; 60 if(!now&&G[x][y]) continue; 61 for(int k=0;k<2;k++) 62 { 63 int xx=x+step[k][0],yy=y+step[k][1]; 64 if(!in(xx,yy)) continue; 65 if(!G[xx][yy]) next=0; 66 vis[xx][yy]=1; 67 } 68 } 69 printf("%d",now); 70 now=next; 71 } 72 printf("\n"); 73 } 74 return 0; 75 }
1010 XYZ and Drops
纯模拟题。比想象的简单好多。
每次出来小水珠加到队列里面。
没出界的也没被吸收的小水珠继续跑。
小水珠跑完后处理一下吸收了小水珠的大水珠。
有爆掉的记下时间。再加上四个方向爆出的小水珠。
传说中改小了数据的题。然而大家都是46ms。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <queue> 5 using namespace std; 6 # define maxn 110 7 int r,c,n,T,map[maxn][maxn]; 8 int d[][2]={{-1,0},{1,0},{0,-1},{0,1}}; 9 queue<int> Q; 10 11 bool in(int x,int y) 12 { 13 return x>0&&x<=r&&y>0&&y<=c; 14 } 15 16 struct node 17 { 18 int x,y,size,t,d; 19 } drop[maxn]; 20 queue<node> q; 21 22 void BFS() 23 { 24 for(int t=1;t<=T;t++) 25 { 26 if(q.empty()) return; 27 while(!Q.empty()) Q.pop(); 28 while(!q.empty()) 29 { 30 node tem=q.front(); 31 if(tem.t>=t) break; 32 q.pop(); 33 int dir=tem.d; 34 int x=tem.x+d[dir][0],y=tem.y+d[dir][1]; 35 if(in(x,y)) 36 { 37 if(drop[map[x][y]].size) 38 { 39 drop[map[x][y]].size++; 40 Q.push(map[x][y]); 41 } 42 else q.push((node){x,y,0,t,dir}); 43 } 44 } 45 while(!Q.empty()) 46 { 47 int N=Q.front(); Q.pop(); 48 node & tem=drop[N]; 49 if(tem.size>4) 50 { 51 tem.size=0; 52 tem.t=t; 53 int x=tem.x,y=tem.y; 54 for(int i=0;i<4;i++) q.push((node){x,y,0,t,i}); 55 } 56 } 57 } 58 return; 59 } 60 61 int main(void) 62 { 63 while((scanf("%d%d%d%d",&r,&c,&n,&T))!=EOF) 64 { 65 memset(map,0,sizeof(map)); 66 for(int i=1;i<=n;i++) 67 { 68 int x,y,size; 69 scanf("%d%d%d",&x,&y,&size); 70 map[x][y]=i; 71 drop[i].x=x; 72 drop[i].y=y; 73 drop[i].size=size; 74 } 75 int sx,sy; scanf("%d%d",&sx,&sy); 76 while(!q.empty()) q.pop(); 77 for(int i=0;i<4;i++) q.push((node){sx,sy,0,0,i}); 78 BFS(); 79 for(int i=1;i<=n;i++) 80 { 81 if(drop[i].size) printf("1 %d\n",drop[i].size); 82 else printf("0 %d\n",drop[i].t); 83 } 84 } 85 return 0; 86 }
1012 ZZX and Permutations
看到是set大法和线段树。于是想尝试一下。
司老大讲过点置换。思路还是懂的。但是看来好几份代码实现都不懂。
后来终于看到了一份码风与我惊人相似的同学的代码。终于懂了。
思路:
字典序最大。就是尽量让大的数字排在前面。
在一个置换的循环节里面。每个数字映射到的是它后面的一个数字。
例如(123)是1->2,2->3, 3->1。
写成原来形式应该是 2 3 1。
整体上应该采取一种贪心的决策。从1开始找能映射到的最大的数字。
对于每个数字。它能采取的映射方案有三种。
1.映射到它自身。
2.映射到它后面一个数字。
3.映射到它之前的一个数字。(正好是一个循环节的末尾)
但是这些情况并不是都成立的。
当后面一个数字已经在别的循环里的时候。就不能取它。
例如 ... x (y ...)...
取前面的数字的时候。不能跨过中间已有的循环节。
例如 ... y (z w) x ...
对于前一种情况。我们只要用一个used数组来标记。
每形成一个完整的循环。就给循环最左端的used置1。
这样就保证了它左边的那个元素不会再次取到它了。
后一种情况就不那么容易了。
我们可以使用一个可以二分的容器。
每当有一个区间被循环占据了。就在容器里添加右端点。
当我们要找一个数左边的最大值时。我们可以先二分出它前面离他最近的一个不能用的点。
再贪心的找这个区间内的最大值。
如何维护区间的最大值呢?
用一个线段树即可。每次把已经用掉的点Delete掉。
代码里看起来没有考虑映射到自身的情况。
那是因为在二分的时候如果不能取得最近的点正好是这个点的前一点。
那么start就指向自己的位置了。相当于包含了这个情况。
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <set> 5 # include <algorithm> 6 using namespace std; 7 # define maxn 100010 8 int p[maxn],ans[maxn],pos[maxn],used[maxn]; 9 set<int> s; 10 set<int>::iterator it; 11 12 struct node 13 { 14 int l,r,Max; 15 }tree[maxn*4]; 16 17 void pushup(int i) 18 { 19 tree[i].Max=max(tree[2*i].Max,tree[2*i+1].Max); 20 } 21 22 void buildtree(int i,int l,int r) 23 { 24 tree[i].l=l; tree[i].r=r; 25 if(l<r) 26 { 27 buildtree(2*i,l,(l+r)/2); 28 buildtree(2*i+1,(l+r)/2+1,r); 29 pushup(i); 30 } 31 else tree[i].Max=p[l]; 32 } 33 34 void Delete(int i,int x) 35 { 36 if(tree[i].l==tree[i].r) 37 { 38 tree[i].Max=0; 39 return; 40 } 41 if(x<=(tree[i].l+tree[i].r)/2) Delete(2*i,x); 42 else Delete(2*i+1,x); 43 pushup(i); 44 } 45 46 int query(int i,int l,int r) 47 { 48 if(tree[i].l>=l&&tree[i].r<=r) return tree[i].Max; 49 int ret=0; 50 if(l<=(tree[i].l+tree[i].r)/2) ret=max(ret,query(2*i,l,r)); 51 if(r>=(tree[i].l+tree[i].r)/2+1) ret=max(ret,query(2*i+1,l,r)); 52 return ret; 53 } 54 55 int main(void) 56 { 57 int T; cin>>T; 58 while(T--) 59 { 60 memset(ans,0,sizeof(ans)); 61 memset(used,0,sizeof(used)); 62 s.clear(); 63 int n; scanf("%d",&n); 64 for(int i=1;i<=n;i++) 65 { 66 scanf("%d",p+i); 67 pos[p[i]]=i; 68 } 69 buildtree(1,1,n); 70 for(int i=1;i<=n;i++) 71 { 72 if(ans[i]) continue; 73 int start=1; 74 if(!s.empty()) 75 { 76 it=s.lower_bound(pos[i]); 77 if(it!=s.begin()) 78 { 79 it--; 80 start=(*it)+1; 81 } 82 } 83 int tem=query(1,start,pos[i]); 84 if(pos[i]<n&&!used[pos[i]+1]&&tem<p[pos[i]+1]) 85 { 86 ans[i]=p[pos[i]+1]; 87 Delete(1,pos[i]+1); 88 } 89 else 90 { 91 ans[i]=tem; 92 used[pos[tem]]=1; 93 for(int j=pos[tem];j<pos[i];j++) 94 ans[p[j]]=p[j+1]; 95 s.insert(pos[i]); 96 } 97 } 98 for(int i=1;i<n;i++) printf("%d ",ans[i]); 99 printf("%d\n",ans[n]); 100 } 101 return 0; 102 }