CF#612(Div.2)
A. Angry Students
题意:
给你一个字符串(只包含’A’,’P’),每过1秒, 对于每一个A,假如它右边那个不是A,它会把他右面那个变成A。问最多过几秒,整个串中A的数目不会增加
思路:
这题数据范围非常小,因此可以用bfs求解
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; int vis[150]; int ans = 0; void solve(int n) { queue<int> q; for(int i = 1;i<=n;++i) if(vis[i]==0) q.push(i); while(!q.empty()) { int x = q.front(); q.pop(); ans = max(ans,vis[x]); if(x+1<=n&&vis[x+1]==-1) { vis[x+1] = vis[x]+1; q.push(x+1); } } return ; } int main() { int t; cin >> t; while(t--) { int n; cin >> n; for(int i = 1;i<=n;i++) vis[i] = -1; string s; cin >> s; for(int i = 0;i<n;i++) { if(s[i]=='A') vis[i+1] = 0; } ans = 0; solve(n); ans = max(ans,0); cout << ans << endl; } return 0; }
B. Hyperset
题意:
给你n个长度为k的字符串(只包括"S", "E", or "T".),对于每三个串,如果这三个串每一个元素完全相同或者完全不同,可以称为一个set,问总共有几对set。
思路:
这里的数据范围是比较小的,我们可以通过枚举两个串然后根据上面的条件构造出第三个串来,检查一下这个串是否存在就行,要注意我们这样枚举的话每一组合是数了3次的,所以要除以3。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 1500+10; string str[maxn]; set<string> s; int main() { int n,k; cin >> n >>k; for(int i = 0;i<n;++i) { cin >>str[i]; s.insert(str[i]); } int all = 'S'+'T'+'E'; string q ; q.resize(k); int res = 0; for(int i = 0;i<n;i++) { for(int j = i+1;j<n;++j) { for(int l = 0;l<k;++l) { if(str[i][l]==str[j][l]) { q[l] = str[i][l]; } else { q[l] = all-str[i][l]-str[j][l]; } } if(s.find(q)!=s.end()) ++res; } } cout << res/3 << endl; return 0; }
C. Garland
题意:
给一个1-n的排列,但排列中的某些数被移去了,,定义一个概念complexity,它的值为排列中奇偶性不同的相邻两数的对数。,要求输出complexity值最小的排列。
思路:
定义一个四维dp数组 dp[i][even][odd][2] ,代表到第i个位置时,已经填了even个偶数,odd个奇数,此时在第i个位置填入(奇数或偶数)的最小答案。
在探讨转移方程之前,我们需要先计算总共空出多少个偶数和奇数,以及在第i个位置之前一共有多少个待填位置,定义为pre[i]。
我们首先要枚举前三维,假如现在枚举的偶数以及奇数数目符合要求,这时如果当前位置已经有数,
如果是奇数,若上一位为偶数,则对答案贡献+1。
f[i][j][k][0] = min(f[i - 1][j][k][0], f[i - 1][j][k][1] + 1);
若是偶数
f[i][j][k][1] = min(f[i - 1][j][k][0] + 1, f[i - 1][j][k][1]);
如果当前位置已经没有数,则需要两种情况都要考虑,同时要注意的时,我们现在是需要往里面填数的,所以转移方程略有不同。
f[i][j][k][0] = min(f[i - 1][j - 1][k][0], f[i - 1][j - 1][k][1] + 1);
f[i][j][k][1] = min(f[i - 1][j][k - 1][0] + 1, f[i - 1][j][k - 1][1]);
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 105; int f[N][N][N][2]={0}; int a[N]; int pre[N]; int odd, even; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); if (a[i] > 0) { if (a[i] % 2 == 0) ++even; else ++odd; pre[i] = pre[i - 1]; } else { pre[i] = pre[i - 1] + 1; } } if (n % 2 == 0) { even = n / 2 - even; odd = n / 2 - odd; } else { even = n / 2 - even; odd = n / 2 + 1 - odd; } memset(f, 0x3f, sizeof f); //第二维和第三维为填充的偶数个数和奇数个数 // cout << 0x3f<< endl; f[0][0][0][0] = 0; f[0][0][0][1] = 0; for (int i = 1; i <= n; ++i) { for (int j = 0; j <= even; ++j) { for (int k = 0; k <= odd; ++k) { if (a[i] > 0) { if (j + k <= pre[i]) { if (a[i] % 2 == 0) { f[i][j][k][0] = min(f[i - 1][j][k][0], f[i - 1][j][k][1] + 1); } else { f[i][j][k][1] = min(f[i - 1][j][k][0] + 1, f[i - 1][j][k][1]); } } } else { if (j + k <= pre[i]) { f[i][j][k][0] = min(f[i - 1][j - 1][k][0], f[i - 1][j - 1][k][1] + 1); f[i][j][k][1] = min(f[i - 1][j][k - 1][0] + 1, f[i - 1][j][k - 1][1]); } } } } } int res = 0x3f3f3f3f; res = min(f[n][even][odd][0], f[n][even][odd][1]); printf("%d\n", res); return 0;
D. Numbers on Tree
题意:
给你一个有根树,对于这棵树,每一个结点上都有一个数字ai,同时定义一个值ci,代表第以第i个结点为根的子树中值比自己大的数的根数,现在给出每个节点的父节点以及ci,请输出YES和任何符合条件的a数组或NO。
思路:
首先通过给出的信息,我们能够构造出这棵树,对于每个结点i,我们可以得到它的子树上有多少个结点,定义为sub[i]。假如子树上的结点数要比给出的c值要小,则一定输出NO,为了简便思考,我们可以假定a数组是1-n的一个排列,对于每一个结点,我们只要保证正好有c[i]个没有被选取的数即可,详细细节可以看代码。
代码:
#include <bits/stdc++.h> using namespace std; const int N = 2e3+3; int n,c[N],ans[N],sub[N]; bool vis[N],f; vector<int>pic[N]; void dfs(int num) { if(!f) return ; int cnt = 0; for(int i = 1;i<=n;++i) { if(!vis[i]) cnt++; if(cnt==c[num]+1) { ans[num] = i; vis[i] =1; break; } } for(int i = 0;i<pic[num].size();++i) { dfs(pic[num][i]); sub[num]+=sub[pic[num][i]]; } if(sub[num]<c[num]) { f = 0; return; } } int main() { while(scanf("%d",&n)!=EOF) { int root,p; for(int i = 0;i<=n;++i) pic[i].clear(); memset(ans,0,sizeof(ans)); memset(vis,0,sizeof(vis)); for(int i =1;i<=n;++i) { scanf("%d %d",&p,&c[i]); if(p==0) root= i; else pic[p].push_back(i); } for(int i = 1;i<=n;++i) { sub[i] = pic[i].size(); } f = 1; dfs(root); if(f) { printf("YES\n"); for(int i = 1;i<=n;++i) { printf("%d%c",ans[i],i==n?'\n':' '); } } else printf("NO\n"); } return 0; }
E1. Madhouse (Easy version)
题意:
交互题,题目给出一个字符串的长度为n,你可对其进行不超过三次询问,格式为
? l r 系统会返回s[l]-s[r]的所有子串(每个子串是乱序的),同时要求总返回的子串数不能超过(n+1)^2。
然后要求输出该字符串是什么,格式为
!s
思路:
这个题事实上只需要询问两次就够了,一个是1-n,另一个是2-n。下面简述下做法
以abcd为例吧
第一次询问
长度为1:a , b , c ,d
长度为2:ab, bc, cd
长度为3:abc,bcd
长度为4:abcd
第二次询问
长度为1:b , c ,d
长度为2:bc, cd
长度为3:bcd
通过观察我们可以发现,
长度为1时,第一次比第二次多一个字符串a,我们这样就得到字符串的第1个元素。
长度为2时,第一次比第二次多出ab,此时a是我们已知的元素,而b正好是第二个元素。
以此类推,就可以得到最终结果。
最后要特判n=1时的情况。
代码:
#include <bits/stdc++.h> using namespace std; vector<string>vec[105]; map<string,int>mp,mp1; string ans; int n; void solve() { ans.resize(n); int res = 0; for(int i = 1;i<=n;++i) { mp1.clear(); string tmp; for(int j = 0;j<vec[i].size();++j) { mp1[vec[i][j]]++; } for(int j = 0;j<vec[i].size();++j) { if(mp.find(vec[i][j])==mp.end()||mp[vec[i][j]]!=mp1[vec[i][j]]) { tmp = vec[i][j]; } } int sum = 0; for(int i = 0;i<tmp.size();++i) { sum+=(tmp[i]-'a'); } ans[i-1] = sum-res+'a'; res = sum; } cout << "! " << ans << endl; } int main() { // int n; cin >> n;string s; if(n==1) { cout << "? 1 1" << endl; cin >> s; cout << "! " << s << endl; } else { cout << "? 1 " << n << endl; for(int i = 0;i<n*(n+1)/2;++i) { cin >> s; sort(s.begin(),s.end()); vec[s.size()].push_back(s); } cout << "? 2 " << n << endl; for(int i = 0;i<n*(n-1)/2;++i) { cin >> s; sort(s.begin(),s.end()); mp[s]++; } solve(); } return 0; }