Codeforces Round #617 (Div. 3)
QAQ:我太难了
A. Even But Not Even
题意:
给你一个长度为n的数组a,你可以从中选择两个数,把其中一个数的值修改为另一个数。使得最后的数组的和是奇数,输出Yes或者No。
思路:
遍历数组,统计奇数个数cnt1,以及偶数个数cnt2和和sum,如果sum是奇数直接输出Yes,否则需要用一个奇数去替换一个偶数,要求cnt1>0且cnt2>0
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 2005; int num[maxn]; int main() { int t; cin >> t; while(t--) { int n; int sum = 0; cin >> n; int cnt1 =0,cnt2 =0; bool flag = false; for(int i =0;i<n;++i) { cin >> num[i]; if(num[i]%2==1) cnt1++; else cnt2++; sum+=num[i]; } if(sum%2==1) { printf("YES\n"); continue; } else { if(cnt1>0&&cnt2>0) { printf("YES\n"); } else printf("NO\n"); } } return 0; }
B. Array Sharpening
题意:
有个人他其实有x卢布,他每次花费y卢布时都会获得y/10(向下取整)卢布,问他最多能花多少卢布。输出这个值。
思路:
通过观察,上面这个花钱的过程实际上就是把当前钱数的个位加到十位上,然后抛去个位。假如这个现在有s卢布,他花一次钱之后的钱数为s/10+s%10,花的钱数为s/10。暴力模拟即可。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main() { int t; cin >> t; while(t--) { LL s; cin >> s; LL ans = s-s%10;; while(s>9) { s = s%10+s/10; ans+=(s-s%10); } cout << ans+s << endl; } return 0; }
C. Mind Control
题意:
给你一个字符串,只包含U,D,L,R,代表一个机器人的移动指令,要求在不改变机器人最终位置的情况下,从字符串中删去一个长度尽可能小的(但不可以是0)的连续子串,输出子串的左下标以及右下标。如果无法删除,则输出-1。
思路:
我们可以先按照字符串进行模拟,并把经过的点以及此时这条指令在字符串中的位置用map记录下来,如果这个点在后面又一次出现,则证明这段区间可以删去,更新一下答案就行,要注意此时同时要更新一下map中相对应的值。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 2e5+100; char s[maxn]; struct Node { int x,y; Node(){} Node(int _x,int _y):x(_x),y(_y){}; bool operator <(const Node&b)const { if(x==b.x) return y<b.y; else return x<b.x; } }; map<Node,int>mp; int main() { int t; cin >> t; while(t--) { mp.clear(); int n; scanf("%d",&n); scanf("%s",s+1); mp[Node(0,0)] =1; int l =1,r=n,x=0,y=0; bool flag = false; for(int i =1;i<=n;++i) { if(s[i]=='D') --y; else if(s[i]=='U') ++y; else if(s[i]=='L') --x; else if(s[i]=='R') ++x; map<Node,int>::iterator it = mp.find(Node(x,y));//cout << x << " "<< y << endl; if(it==mp.end()) { mp[Node(x,y)] = i+1; } else { flag = true; int l1 = it->second,r1 = i; //cout << l1 << " " <<r1 << " " <<l << " " <<r << endl; if(r1-l1<r-l) { l =l1; r = r1; } it->second = i+1; } } if(flag) printf("%d %d\n",l,r); else printf("-1\n"); } return 0; }
D. Fight with Monsters
题意:
有n个怪兽站在一排让你和你的对手打,每次你和你的对手只可以按照顺序进行战斗,每次都是你先攻击,然后是你的对手进行过攻击,循环往复,知道怪兽被击倒,你的攻击是a,你对手的攻击是b,第i只怪兽的血量是hi,对于每只怪兽,如果最后一击是你打出的,你的分数+1,如果是你对手打出的,则你的分数不变,你有k次机会可以跳过你对手的回合,输出你可能得到最大的分数。
思路:
首先,对于那些一定是你击倒的怪兽,肯定是不用机会的,使用机会的时候一定是你打完怪兽之后下一回合对手正好能把怪兽打死的时候,我们可以把这种情况挑出来,然后按照怪兽剩余血量从小到大排序,从头遍历,计算一下在k次最后一共能打死几只怪兽即可。细节可以看代码。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 2e5+100; LL h[maxn]; vector<LL>vec; int main() { LL n,a,b,k; cin >> n >>a >>b >>k; for(int i =1;i<=n;++i) cin >> h[i]; LL ans = 0; for(int i = 1;i<=n;++i) { LL tmp = h[i]%(a+b); if(tmp==0) tmp =b; else if(tmp<=a) { ++ans; continue; } else { tmp-=a; } vec.push_back(tmp); } sort(vec.begin(),vec.end()); int len = vec.size(); for(int i = 0;i<len;++i) { if(k<0) break; LL tmp = vec[i]/a; if(vec[i]%a!=0) ++tmp; if(k<tmp) break; else { k-=tmp; ++ans; } } cout << ans << endl; return 0; }
E. String Coloring
题意:
E1
给你一个字符串,要求你把每个字符涂上0或者1两种颜色,假如相邻的字符颜色不同则可以交换,则求一种涂色方案能够使得涂完色之后,该字符串能够通过交换变成有序的。
可以,输出Yes以及涂色方案,否则输出No。
E2
给你一个字符串,要求对于每一个字符串涂上颜色,相邻的字符颜色不同则可以交换,问最少需要几种颜色使得涂完色之后,该字符串能够通过交换变得有序。输出最小需要的颜色数以及涂色方案。
思路:
E1
首先,我们要知道,涂有相同颜色的两个字符是不能交换的,所以我们选择涂有相同颜色的字符在原字符串中应该保持非递减的顺序,那么问题就变成了检查原先的字符串能否拆分成两个非递减的序列。
E2
先决条件如同上面,问题只不过变成了原先的字符串最少拆分成几个子序列,解法与E1类似。
代码:
E1
#include <bits/stdc++.h> using namespace std; int main() { int n; string s; cin >> n >> s; string ans = ""; char a = 'a',b = 'a'; for(int i =0;i<n;++i) { if(s[i]>=a) { a = s[i]; ans+='0'; } else { if(s[i]>=b) { b = s[i]; ans+='1'; } else { printf("NO\n"); return 0; } } } printf("YES\n"); cout << ans << "\n"; return 0; }
E2
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5+100; int ans[maxn]; int main() { int n; string s; cin >> n >> s; int d = 0,cur = 0; while(d<n) { ++cur; char mx = 'a'; for(int i = 0;i<n;++i) { if(!ans[i]) { if(s[i]>=mx) { mx = s[i]; ans[i] = cur; ++d; } } } } printf("%d\n",cur); printf("%d",ans[0]); for(int i = 1;i<n;++i) printf(" %d",ans[i]); printf("\n"); return 0; }
F. Berland Beauty
题意:
给你一个结点数为n的无向有边权的树,先给出n-1条边,然后给出m个数据,每个数据的格式为 a, b, g,代表从点a到点b所经过的每条边的权值的最小值为g,让你根据上面输入的n-1条边的顺序,输出每条边的权值,如果无解输出-1。
思路:
我们可以把每条数据按照最小权值从大到小排一遍,然后用LCA去给每条经过的边赋值,同时check一下答案是否正确,对于没经过的边,随便赋值即可。
代码:
#include <bits/stdc++.h> #define ms(a,b) memset(a,b,sizeof(a)) #define INF 1e6 #define pb push_back using namespace std; typedef long long LL; typedef double db; const int N = 5e3+5; struct node { int v,i; }; vector<node>tr[N]; int val[N],f[N],d[N],cnt[N],ans[N]; struct ask { int a,b,w; bool operator<(const ask A)const { return w<A.w; } }; priority_queue<ask> q; void dfs(int u,int fa) { d[u] = d[fa]+1; f[u] = fa; for(int i = 0;i<tr[u].size();++i) { int v = tr[u][i].v; if(v==fa) continue; dfs(v,u); cnt[v] = tr[u][i].i; } val[u] =INF; } bool work(int x,int y,int w) { int minn = INF; if(d[x]<d[y])swap(x,y); while(d[x]!=d[y]) { if(val[x]==INF) val[x] = w; minn = min(minn,val[x]); x = f[x]; } while(x!=y) { if(val[x]==INF) val[x] = w; minn = min(minn,val[x]); x = f[x]; if(val[y]==INF) val[y] = w; minn = min(minn,val[y]); y = f[y]; } return minn==w; } int main() { int n,m; cin >> n; for(int i = 1,u,v;i<n;++i) { cin >>u >>v; tr[u].pb({v,i}); tr[v].pb({u,i}); } dfs(1,0); cin >> m; for(int i = 0,a,b,w;i<m;++i) { cin >> a >>b >> w; q.push({a,b,w}); } while(!q.empty()) { ask tmp = q.top(); q.pop(); while(!work(tmp.a,tmp.b,tmp.w)) { puts("-1"); return 0; } } for(int i = 2;i<=n;++i) ans[cnt[i]] = val[i]; for(int i = 1;i<n;++i) cout << ans[i] << " "; return 0; }