Codeforces Round #547 (Div. 3)
题目链接:https://codeforces.com/contest/1141
A,B咕咕了...
C. Polycarp Restores Permutation
题意:
有一个n的排列,但现在只给出相邻两位的差,问原排列是多少,如果不存在就输出-1。
题解:
通过相邻两位的差我们可以知道第一个元素和其它位置元素的大小关系,然后根据这个来搞一波就行了。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5 + 5; int n; int a[N], b[N], c[N]; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for(int i = 1; i < n; i++) cin >> a[i]; b[1] = 0; for(int i = 1; i < n; i++) { b[i + 1] = b[i] + a[i]; } int mn = *min_element(b + 1, b + n + 1); for(int i = 1; i <= n; i++) b[i] += 1 - mn; for(int i = 1; i <= n; i++) c[i] = b[i]; sort(c + 1, c + n + 1); int check = 0; for(int i = 1; i <= n; i++) { if(c[i] != i) check = 1; } if(check) { cout << -1; } else { for(int i = 1; i <= n; i++) cout << b[i] << " "; } return 0; }
D. Colored Boots
题意:
给出两个字符串,然后问这两个字符串中字符的最大匹配是多少,并且输出相应的匹配。两个字符串中的字符可以匹配,要么两个字符相等,要么其中一个是"?"。
题解:
这个题我的做法就是对第二个字符串中的字符插入一个pair型的set里面,然后通过二分来搞,当然还有一点暴力。反正就是分几种情况匹配就是了。
但其实可以直接将所有种类的字符作为链表头,然后每次插入其对应的位置,最后一次来匹配就行了,具体实现方法就是利用队列或者vector。
给出我的代码吧...
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; const int N = 150010; int n; char s[N], t[N]; int vis[N]; set <pair<char, int > > S; vector <pair<int, int> >g; int main() { cin >> n; scanf("%s%s", s + 1, t + 1); for(int i = 1; i <= n; i++) { S.insert(mp(t[i], i)); } int ans = 0; for(int i = 1; i <= n; i++) { char c = s[i]; if(c != '?') { auto it = S.lower_bound({c, -1}); if(it == S.end()) continue ; if(it->first == c) { ans++; g.push_back(mp(i, it->second)); S.erase(it); vis[i] = 1; } } } int i = 1; while(!S.empty()) { auto it = S.begin(); char c = it->first; int id = it->second; if(c != '?') break ; for(; i <= n; i++) { if(vis[i] || s[i] == '?') continue ; ans++; g.push_back(mp(i, id)); S.erase(it); i++; break ; } if(i == n + 1) break ; } i = 1; while(!S.empty()) { for(; i <= n; i++) { if(!vis[i] && s[i] == '?') { ans++; auto it = S.begin(); g.push_back(mp(i, it->second)); vis[i] = 1; S.erase(it); i++; break ; } } if(i == n + 1) break ; } printf("%d ", ans); for(auto v : g) { printf("%d %d ", v.first, v.second); } return 0; }
E. Superhero Battle
题意:
给出怪兽的初始血量,然后有无穷多个回合,每个回合怪兽都会损失或者增加一些血量,但是这些回合是有循环节的,现在给出循环节的具体信息。
问最后怪兽的血量是否能够小于等于0,如果可以,就输出怪兽最早是在哪一个回合阵亡的。
题解:
首先预处理出一个前缀和,然后特别判断一下第一轮的情况以及不会阵亡的情况。
然后我的做法就是枚举一下这个怪兽在1~n-1哪个位置会阵亡,然后通过这个算回合数,最终维护一下答案就行了。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; int n; ll a[N],sum[N]; ll h; int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>h>>n; for(int i=1;i<=n;i++){ cin>>a[i]; sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=n;i++){ if(h+sum[i]<=0){ cout<<i; return 0; } } if(sum[n]>=0){ cout<<-1; return 0; } // ll tmp = h; ll ans = 1e18; for(ll i=1;i<=n;i++){ tmp = h ; tmp+=sum[i]; ll now = abs(sum[n]); ll cnt = (tmp-1)/now+1; ans=min(ans,cnt*n+i); } cout<<ans; return 0; }
F2. Same Sum Blocks (Hard)
题意:
给出n个数,然后求数量最多的,并且区间和相等,不交叉的区间,最后将区间信息输出出来。
题解:
由于n的范围不是很大,所以我们可以采用稍暴力一点的做法,直接将所有区间的信息:包括左右端点、区间和存起来,用一个pair保存左右端点,然后放入一个一维为sum的vector里面,然后暴力来求就是了。当然由于区间和可能比较大,所以可以离散化一波,或者用map来保存。
之后就是对于每一个和,找到他所有的区间,来更新答案就是了。这里有一个技巧,如果我们是按照左端点来排序的话,贪心起来有点困难;贪心的最优选择还是按照右端点来排序,所以一开始我们存进pair里面的时候,把左右端点对调一下,这样后面sort的时候就是优先对右端点进行排序了。
代码如下:
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1505; int n; int a[N], sum[N], b[N*N]; vector <pii> g[N * N], T, res; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for(int i = 1; i <= n; i++) { cin >> a[i]; sum[i] = sum[i - 1] + a[i]; } int D = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= i; j++) { b[++D] = sum[i] - sum[j - 1]; } } sort(b + 1, b + D + 1); D = unique(b + 1, b + D + 1) - b; for(int i = 1; i <= n; i++) { for(int j = 1; j <= i; j++) { int p = lower_bound(b + 1, b + D + 1, sum[i] - sum[j - 1]) - b; g[p].emplace_back(mp(i, j)); } } int l, ans = 0, p; for(int i = 0; i < N * N; i++) { if((int)g[i].size() != 0) { l = 0; T.clear(); sort(g[i].begin(), g[i].end()); int pre = -1; for(int j = 0; j < g[i].size(); j++) { if(g[i][j].second > pre) { T.emplace_back(g[i][j]); pre = g[i][j].first; } } if((int)T.size() > (int)res.size()) { swap(T, res); } } } cout << (int)res.size() << ' ';; for(int i = 0; i < res.size(); i++) { cout << res[i].second << " " << res[i].first << ' '; } return 0; }
G. Privatization of Roads in Treeland
题意:
给一颗树染色,然后问最少需要几种颜色。若与一个点相接的几条边有重复的颜色,那么这个点被叫做“怒气”点。题目中会给出k这个限制条件,意即“怒气”点不超过k个。
题解:
二分有多少个“怒气”点,或者也直接贪心地取k个怒气点就行,这k个怒气点都是度数最大的那几个点。
然后dfs染色一下就行了,顺便记录一下答案。因为此时是保证有解的。
代码如下:
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; const int N = 2e5+5; int n,k; vector <pair<int,int> > g[N]; int du[N]; int ans[N]; int l,r,mid; bool check(int x){ int cnt = 0; for(int i=1;i<=n;i++){ if(du[i]>x) cnt++; } return cnt<=k; } void dfs(int u,int fa,int cnt){ for(auto to:g[u]){ int v = to.first,id = to.second; if(v==fa) continue ; ans[id]=cnt++;; if(cnt>r) cnt = 1; dfs(v,u,cnt); } } int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n>>k; for(int i=1;i<n;i++){ int u,v; cin>>u>>v; g[u].push_back(mp(v,i)); g[v].push_back(mp(u,i)); du[u]++;du[v]++; } l=1,r=N; while(l<r){ mid = (l+r)>>1; if(check(mid)) r=mid; else l=mid+1; } cout<<r<<' '; dfs(1,-1,1); for(int i=1;i<n;i++) cout<<ans[i]<<" "; return 0; }