2016-2017 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2016)
A Artwork
题目链接
题意:给你一个n*m的图,在这张图上选择(x1,y1)到(x2,y2)的矩形涂黑,问每一步之后剩下联通的白色块有多少个。
思路:并查集每个联通块,从后往前类撕封条的方法,将每个从黑变白的块与四周并查集,记录答案。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e3 + 5; struct node { int x1, x2, y1, y2; }op[10005]; int g[maxn][maxn]; int fa[maxn*maxn]; int ans[10005]; int dir[2][2] = { -1,0, 0,-1 }; int dir2[4][2] = { -1, 0, 0, -1, 1, 0, 0, 1}; int n, m, p; int ok(int x, int y) { return x > 0 && y > 0 && x <= n && y <= m; } int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } void mix(int x, int y) { int fx = find(x); int fy = find(y); fa[fx] = fy; } void init() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { int id = (i - 1) * m + j; fa[id] = id; } } } int main() { scanf("%d%d%d", &n, &m, &p); init(); for (int i = 1; i <= p; i++) { scanf("%d%d%d%d", &op[i].x1, &op[i].y1, &op[i].x2, &op[i].y2); if (op[i].x1 == op[i].x2) { for (int y = op[i].y1; y <= op[i].y2; y++) { g[op[i].x1][y] ++; } } else { for (int x = op[i].x1; x <= op[i].x2; x++) { g[x][op[i].y1] ++; } } } // cout << "yyy" << endl; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { for (int k = 0; k < 2; k++) { int x = dir[k][0] + i; int y = dir[k][1] + j; if (ok(x, y) && !g[i][j] && !g[x][y]) { mix(((x - 1)*m + y), (i - 1)*m + j); } } } } set<int>st; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if(!g[i][j]){ int id = (i - 1) * m + j; fa[id] = find(fa[id]); st.insert(fa[id]); // cout << i<<' '<<j << " *** " << fa[id] << endl; } } } ans[p] = st.size(); for (int i = p; i >1; i--) { ans[i-1] = ans[i]; if (op[i].x1 == op[i].x2) { for (int y = op[i].y1; y <= op[i].y2; y++) { g[op[i].x1][y] --; // cout <<"!!"<< op[i].x1 << ' ' << y << ' ' << g[op[i].x1][y] << endl; if(!g[op[i].x1][y]){ int id = (op[i].x1 - 1) * m + y; fa[id] = id; ans[i-1]++; for (int k = 0; k < 4;k++){ int tx=op[i].x1+dir2[k][0]; int ty = y + dir2[k][1]; if(ok(tx,ty)&&!g[tx][ty]){ if(find((tx - 1) * m + ty)!=find(id)){ mix((tx - 1) * m + ty, id); ans[i-1]--; } } } } } } else { for (int x = op[i].x1; x <= op[i].x2; x++) { g[x][op[i].y1] --; // cout << "??" << x << ' ' << op[i].y1 << ' ' << g[x][op[i].y1] << endl; if(!g[x][op[i].y1]){ int id = (x - 1) * m + op[i].y1; fa[id] = id; ans[i-1]++; for (int k = 0; k < 4;k++){ int tx=x+dir2[k][0]; int ty = op[i].y1 + dir2[k][1]; // cout << tx << " " << ty <<" "<<g[tx][ty]<< endl; if(ok(tx,ty)&&!g[tx][ty]){ if(find((tx - 1) * m + ty)!=find(id)){ mix((tx - 1) * m + ty, id); ans[i-1]--; } } } } } } // cout << ans[i - 1] << endl; } for (int i = 1;i<=p;i++) printf("%d\n", ans[i]); // system("pause"); return 0; } /* 4 6 5 2 2 2 6 1 3 4 3 2 5 3 5 4 6 4 6 1 6 4 6 */
B Bless You Autocorrect!
题目链接
题意:给定若干个字符串,可以打出若干个字符+tab进行自动补全,给你一个字符串求如何打步数最小
思路:字典树建边+bfs,注意单向边和双向边不同
#include <bits/stdc++.h> #define maxn 1000005 using namespace std; int n,m; char s[maxn]; int trie[maxn][30]; int tot=0; int root; vector <int>G[maxn]; int val[maxn]; int step[maxn]; int pre[maxn]; void insert(){ int len=strlen(s); root=0; for(int i=0;i<len;i++){ int id=s[i]-'a'; if(!trie[root][id]) trie[root][id]=++tot; val[trie[root][id]]++; G[root].push_back(trie[root][id]); G[trie[root][id]].push_back(root); pre[trie[root][id]]=root; root=trie[root][id]; } int lst=root; for(int i=lst;i!=0;i=pre[i]){ if(i==lst)continue; if(val[i]==1){ G[i].push_back(lst); }else break; } } void find(){ int len=strlen(s); root=0; int ans=0; for(int i=0;s[i];i++){ int x=s[i]-'a'; if(trie[root][x]==0) break; root=trie[root][x]; ans++; } if(!root) printf("%d\n",len); else printf("%d\n",len-ans+step[root]); } void bfs(){ queue <int>q; q.push(0); step[0]=0; while(!q.empty()){ int f=q.front(); q.pop(); for(int i=0;i<G[f].size();i++){ if(G[f][i]==f)continue; if(!step[G[f][i]]){ step[G[f][i]]=step[f]+1; q.push(G[f][i]); } } } } void input(){ scanf("%d%d",&n,&m); for(int i=0;i<n;i++){ scanf("%s",s); insert(); } bfs(); } void solve(){ for(int i=0;i<m;i++){ scanf("%s",s); find(); } } int main(){ input(); solve(); } /* 5 5 austria autocorrect program programming computer autocorrelation */
C Card Hand Sorting
题目链接
题意:52张扑克,现在给你n张牌,要求最后排序的方式是同一类花色在一块并且单调递增,求最小需要更改几张顺序。
思路:52张扑克同一花色连续且递增的方案可以枚举写出,对于给定的序列,和枚举出的结果进行求最长公共子序列。n-得到最大答案就是答案。
#include <bits/stdc++.h> using namespace std; char hs[4] = {'s', 'h', 'd', 'c'}; int hs1[4] = {0,1, 2, 3}; char val[13] = {'2', '3', '4', '5', '6', '7', '8', '9','T', 'J', 'Q', 'K', 'A'}; char str[55][3]; char cmpstr[4][13][5]; int n, dp[102][102]; void input(){ scanf("%d", &n); for (int i = 1; i <= n;i++){ scanf("%s", str[i]); } // for (int i = 1; i <= n;i++){ // printf("%s ", str[i]); // } int maxx = -1; do { int flag[4] = {0}; for (int i = 0; i < 16; i++) { for (int j = 0; j < 4; j++) { if ((i & (1 << j) )== 0) flag[j] = 0; else flag[j] = 1; } int cnt = 0; // for (int j = 0; j < 4;j++) // printf("%d:%d\n", j, flag[j]); for (int j = 0; j < 4;j++){ cnt = 0; if(flag[j]==0){ for (int k = 0; k < 13;k++){ cmpstr[j][cnt][0] = val[k]; cmpstr[j][cnt][1] = hs[hs1[j]]; cmpstr[j][cnt][2] = '\0'; cnt++; } }else { for (int k = 12; k >=0;k--){ cmpstr[j][cnt][0] = val[k]; cmpstr[j][cnt][1] = hs[hs1[j]]; cmpstr[j][cnt][2] = '\0'; cnt++; } } } // for (int j = 0; j < 4;j++){ // for (int k = 0; k < cnt; k++) // printf("%s", cmpstr[j][k]); // } // printf("\n"); for (int j = 0; j < 4;j++){ for (int l = 0; l< cnt;l++){ for (int k = 1; k <= n;k++){ // cout<<cmpstr[j][l]<<" "<<str[k]<<endl; if(strcmp(cmpstr[j][l],str[k])==0) dp[j * cnt + l + 1][k - 1 + 1] = dp[j * cnt + l][k - 1] + 1; else dp[j * cnt + l + 1][k - 1 + 1] = max(dp[j * cnt + l+1][k - 1], dp[j * cnt + l][k - 1+1]); } } } maxx = max(maxx, dp[4*cnt][n]); } } while (next_permutation(hs1, hs1 + 4)); printf("%d", n - maxx); } int main(){ input(); // solve(); } /* 4 2h Th 8c Qh */
D Daydreaming Stockbroker
题目链接
题意:股票模拟,主人公有100元钱,给出n天里每天的股票价格,即x元1股,主人公同时持有股不得超过1e5,问主人公最终最多能持有多少钱。
思路:最低点买入最高点卖出
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<set> #include<queue> #include<algorithm> #include<string> #include<cmath> #include<stack> #include<functional> #include<bitset> #define ll long long #define maxn 100002 const int mod = 1e9 + 7; using namespace std; int main(void){ ll num[402]; ll t, i, has = 0, price, money = 100; scanf("%lld",&t); for(i = 0;i < t;i++){ scanf("%lld",&num[i]); } for(i = 0;i < t - 1;i++){ if(num[i] < num[i + 1]){ if(has < 100000){ if(100000 * num[i] < money){ price = num[i]; has = 100000; money -= num[i] * 100000; } else{ price = num[i]; has += money / num[i]; money %= num[i]; } } } else{ money += has * num[i]; has = 0; } } if(num[t - 1] >= price){ money += has * num[t - 1]; } else{ money += has * t; } printf("%lld\n",money); return 0; }
E Exponial
题目链接
题意:欧拉降幂
F Fleecing the Raffle
题目链接
题意:现在一个抽奖箱里面有n张写有n个人名字的卡片,需要从中抽取p张作为得奖主。现在你想在抽奖箱中加入k张写有你的名字的卡片,你需要确定一个k,使得抽到你且仅抽到你一次的概率最大。并输出该最大概率。
思路:概率化简计算
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<set> #include<queue> #include<algorithm> #include<string> #include<cmath> #include<stack> #include<functional> #include<bitset> #define ll long long #define maxn 100002 const int mod = 1e9 + 7; using namespace std; int main(void){ double pre = 0; int n, p, i, j; scanf("%d %d",&n,&p); for(i = 1;;i++){ double temp = 1.0 * i * p / (n - p + 1); for(j = 0;j < p;j++){ temp = temp * (n - j) / (n - j + i); } if(temp > pre){ pre = temp; } else{ break; } } printf("%.10f\n",pre); return 0; }
G Game Rank
题目链接
题意:炉石的晋级规则,给出胜利和失败的次数求最后等级
思路:模拟
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<set> #include<queue> #include<algorithm> #include<string> #include<cmath> #include<stack> #include<functional> #include<bitset> #define ll long long #define maxn 100002 const int mod = 1e9 + 7; using namespace std; char ch[10002]; int rk, star, win; void add(int x){ star += x; if(rk > 20){ if(star > 2){ star -= 2; rk--; } } else if(rk > 15){ if(star > 3){ star -= 3; rk--; } } else if(rk > 10){ if(star > 4){ star -= 4; rk--; } } else if(rk){ if(star > 5){ star -= 5; rk--; } } } void sub(int x){ if(rk < 20&&rk||rk == 20&&star){ star--; if(star < 0){ rk++; if(rk <= 10){ star = 4; } else if(rk <= 15){ star = 3; } else{ star = 2; } } } } int main(void){ int i, len; scanf("%s",ch); strlen(ch); rk = 25; star = win = 0; len = strlen(ch); for(i = 0;i < len;i++){ if(ch[i] == 'W'){ win++; if(win >= 3&&rk >= 6){ add(2); } else{ add(1); } } else{ win = 0; sub(1); } } if(rk){ printf("%d\n",rk); } else{ puts("Legend"); } return 0; }
J Jumbled Compass
题目链接
题意:指南针 给出原度数和现度数,求旋转角度差
思路:水
#include <bits/stdc++.h> #define inf 0x3f3f3f3f3f3f3f3f #define maxn 1005 typedef long long ll; using namespace std; int n1, n2; void input(){ scanf("%d%d", &n1, &n2); int ans = n2 - n1; if(ans<0) ans+=360; if(ans>180) ans = ans - 360; printf("%d\n", ans); } int main(){ input(); // solve(); // printf("\n"); // system("pause"); return 0; }