2016-2017 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2016)
Time:2018.4.3 18:00 - 22:00
A Artwork solved by ym solved by czh
题意
给一个n×m一个网格,有q个询问,每次询问有两个点坐标,表示从第一个点到第二个点都被标记(这两个点x/ y相同),对于q个询问,每次输出网格共被分成几个区域(1<=n,m<=1e3,q<=10000)
分析
比赛的时候对于每个q直接bfs不出意外的tle的起不来,没有好的思路,死掉
赛后:联通块应该想到并查集来解决
对询问倒着作,并查集维护联通块即可
hash函数写反错了一年才找出来 by ym
赛后学长指点了一下,但写出来后超时,原来是并查集路径压缩错了,以前用的路径压缩都不是很快 by czh
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn = 1000+7; const int maxm = 1e6+200; int dx[5]={1,-1,0,0}; int dy[5]={0,0,-1,1}; int n,m,q; int num[maxn][maxn]; int answer[maxm]; int fa[maxm]; int x1[maxn*10], y1[maxn*10], x2[maxn*10], y2[maxn*10]; void init() { for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { fa[(i-1)*m+j]=(i-1)*m+j; } } } int findfa(int x) { if(x==fa[x]) return x; else return fa[x]=findfa(fa[x]); } int tot=0; void join(int x,int y) { int xx=findfa(x); int yy=findfa(y); if(xx!=yy) { fa[yy]=xx; tot--; } } void ok(int x,int y) { for(int i=0;i<4;i++) { int xx=x+dx[i]; int yy=y+dy[i]; if(xx>=1 && xx<=n && yy>=1 && yy<=m && num[xx][yy]==0) { join((xx-1)*(m)+yy, (x-1)*m+y); } } } int main() { scanf("%d%d%d", &n, &m, &q); init(); for(int i=1;i<=q;i++) { scanf("%d%d%d%d", &x1[i], &y1[i], &x2[i], &y2[i]); if(x1[i]==x2[i]) { for(int j=y1[i];j<=y2[i];j++) { num[x1[i]][j]++; } } else { for(int j=x1[i];j<=x2[i];j++) { num[j][y1[i]]++; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(num[i][j]==0) { ok(i,j); tot++; } } } for(int i=q;i>=1;i--) { answer[i]=tot; if(x1[i]==x2[i]) { for(int j=y1[i];j<=y2[i];j++) { --num[x1[i]][j]; if(num[x1[i]][j]==0) { tot++; ok(x1[i],j); } } } else { for(int j=x1[i];j<=x2[i];j++) { --num[j][y1[i]]; if(num[j][y1[i]]==0) { tot++; ok(j,y1[i]); } } } } for(int i=1;i<=q;i++) printf("%d\n", answer[i]); return 0; }
B Bless You Autocorrect! solved by none
题意
给一本有n个单词的词库具有自动补全功能,优先级从高到低,q个询问,问每个询问的单词的最小输入(1<=n<=1e5,1<=q<=1e5)
注意:不存在一个单词由多个单词共同构成
分析
将所有单词构成一颗字典树,根据优先级建好边后,在树上跑个tree dp
目前码不出来,留坑
zls代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; struct Node { int nxt[26]; int jump; int fa; }; Node trie[2000005]; int n,m; string s[100005]; int en[100005]; int dist[2000005]; queue<int> q; int num=0; int insert(string s) { int now=0,dq; for (int i=0;i<s.size();++i) { int dq=s[i]-'a'; if (trie[now].nxt[dq]==-1) { ++num;trie[now].nxt[dq]=num; trie[num].fa=now; for (int j=0;j<26;++j) trie[num].nxt[j]=-1; trie[num].jump=-1; add(now,num,1); add(num,now,1); } now=trie[now].nxt[dq]; } return now; } void addedge(string s,int en) { int now=0,dq; for (int i=0;i<s.size();++i) { int dq=s[i]-'a'; if (trie[now].jump==-1) { add(now,en,1); trie[now].jump=en; } now=trie[now].nxt[dq]; } if (trie[now].jump==-1) { trie[now].jump=en; add(now,en,1); } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) cin>>s[i]; for (int i=0;i<26;++i) trie[0].nxt[i]=-1; trie[0].jump=0,trie[0].fa=-1; for (int i=1;i<=n;++i) { int tmp=insert(s[i]); addedge(s[i],tmp); } for (int i=1;i<=m;++i) { cin>>s[0]; en[i]=insert(s[0]); } for (int i=0;i<=num;++i) dist[i]=1e9; dist[0]=0; while (!q.empty()) q.pop(); q.push(0); trie[0].jump=-1; while (!q.empty()) { int x=q.front();q.pop(); for (int i=0;i<26;++i) if (trie[x].nxt[i]!=-1) { int y=trie[x].nxt[i]; if (dist[y]!=1e9) continue; dist[y]=dist[x]+1; q.push(y); } if (trie[x].fa!=-1) { int y=trie[x].fa; if (dist[y]==1e9){ dist[y]=dist[x]+1; q.push(y);} } if (trie[x].jump!=-1) { int y=trie[x].jump; if (dist[y]==1e9) { dist[y]=dist[x]+1; q.push(y);} } } for (int i=1;i<=m;++i) printf("%d\n",dist[en[i]]); return 0; }
C Card Hand Sorting solved by czh
题意
有n张牌排成一行,每张牌两个属性,第一个数字/字母代表它在这一类的rank,后一个字母代表它的种类,共有13个rank和4种牌,现要求同种牌必须放在相邻位置,每种牌内部是有序的,问最少需要移动几次(移动是任意的)(n<=52)
分析
ym:暴力枚举+maskdp,待补
czh:注意到n最大为52,枚举花色的顺序和花色内的顺序,然后求最大子序列
#include<bits/stdc++.h> using namespace std; map<char,int>mp; char color[60],a[3]; int pe[4]={1,2,3,4},ind[60],n,num[60]; map<char,int>ma; int dp[60]; int LIS() { memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) dp[i]=1; int maxx=0; for(int i=1;i<=n;i++) { for(int j=1;j<i;j++) { if(num[i]>num[j]) dp[i]=max(dp[i],dp[j]+1); } maxx=max(maxx,dp[i]); } return maxx; } int main() { ma['s']=0;ma['h']=1;ma['d']=2;ma['c']=3; cin>>n; for(int i=1;i<=n;i++) { scanf("%s",a); if(a[0]>='0'&&a[0]<='9') ind[i]=a[0]-'0'; else if(a[0]=='T')ind[i]=10; else if(a[0]=='J')ind[i]=11; else if(a[0]=='Q')ind[i]=12; else if(a[0]=='K')ind[i]=13; else if(a[0]=='A')ind[i]=14; color[i]=a[1]; } int ans=0; do{ for(int i=0;i<16;i++) { for(int j=1;j<=n;j++) { num[j]=pe[ma[color[j]]]*1000; if(((1<<ma[color[j]])&i)==0) num[j]-=ind[j]; else num[j]+=ind[j]; } ans=max(ans,LIS()); } }while(next_permutation(pe,pe+4)); cout<<n-ans<<endl; }
D Daydreaming Stockbroker solved by ym solved by czh
题意
给你n天的物价,初始有100元,库存量最大不能超过1e5,问最后做多可以有多少钱
对比:http://codeforces.com/contest/867/problem/E
分析
此题感觉能挣钱就买卖,低进高出即可
CF867E,一天只能买或者卖
#include<bits/stdc++.h> #define ll long long using namespace std; int a[370]; int main() { int n; scanf("%d", &n); for(int i=1;i<=n;i++) scanf("%d", &a[i]); ll sum=100; for(int i=1;i<n;i++) { if(a[i+1]>a[i]) { ll num=min(sum/a[i], 100000LL); sum=sum-num*a[i]+num*a[i+1]; } } printf("%lld\n", sum); return 0; }
E Exponial
数论题
F Fleecing the Raffle solved by czh&ym
题意
现在有n个人抽奖,共有p个奖,现在可以将任意数量自己的名字放入箱子里,问自己最大的中奖概率
分析
设添加x个自己,则中奖概率为 C(x,1) * C(n,p-1) / C(n+x,p)
组合式推一下,化简一下不难看出规律,暴力找最大值即可
G Game Rank solved by czh solved by ym
题意
模拟题
分析
ym:按题意模拟即可
czh:题目很绕,没有很快理解出题意 by czh
H Highest Tower
DIfficult
I Interception
DIfficult
J Jumbled Compass solved by czh solved by ym
签到题
K Keeping the Dogs Apart
DIfficult
Summary
Ym:训练的时候很困=_=,没状态,还读错题了,坑了,和学弟一起训练体验很好呀
读题能力不行,需要及时背单词,提升读题能力
提升思维能力
Czh: 英语水平要求很高,一两个关键单词认错往往这题就过不了