Codeforces #312 div2 only
2015-07-15 17:28:04
【传送门】
总结:... 打星的场 赛中 PP 了ABCE,后来C被FST(TLE)了。
A题... 开场不是很认真... 23min+2WA - -。然后开了C题,用map搞过了,但复杂度n×(logn)^3,最后导致TLE,不能用map。
随后过了B题。 由于D题比较长... 直接看了E,相了一会发现是线段树暴力,敲了30+min竟然过了编译就AC了,有点意外- -。
D题没敲完。。赛后用了两种方法补了一下,细节很多,考虑需要很周全,是个好题!
A题:排序
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; int n; pii g[110]; vector<pii > g1,g2; int main(){ scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%d%d",&g[i].first,&g[i].second); } sort(g + 1,g + n + 1); int cnt1 = 0,cnt2 = 0; for(int i = 1; i <= n; ++i){ if(g[i].first > 0){ cnt2++; g2.push_back(g[i]); } } for(int i = n; i >= 1; --i){ if(g[i].first < 0){ cnt1++; g1.push_back(g[i]); } } int cnt = min(cnt1,cnt2); int sum = 0; for(int i = 0; i < cnt; ++i){ sum += g1[i].second; sum += g2[i].second; } int maxx = 0; if(cnt1 != cnt2){ if(cnt1 > cnt2) sum += g1[cnt].second; else sum += g2[cnt].second; } printf("%d\n",sum); return 0; }
B题:小技巧
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; int n; int st[1000010],ed[1000010],cnt[1000010]; int main(){ int a; scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%d",&a); if(st[a] == 0) st[a] = i; ed[a] = i; cnt[a]++; } int tmax = 0; for(int i = 1; i <= 1000000; ++i) tmax = max(tmax,cnt[i]); int ans = INF,ansl,ansr; for(int i = 1; i <= 1000000; ++i) if(cnt[i] == tmax){ if(ans > ed[i] - st[i] + 1){ ans = ed[i] - st[i] + 1; ansl = st[i]; ansr = ed[i]; } } printf("%d %d\n",ansl,ansr); return 0; }
C题:暴力+统计
题意:有n(n<=10^5)个数 A[1] ~ A[n],每次操作可以把一个数乘上2或者除以2(向下取整),问最少多少步可以使得每个数都相等。
思路:对于每个数,先看其所有能到达的数,以及到达这些数的最小步数(累加 vis[] 数组),方法是将这个数不断地除以2,根据到达的点来更新数组。
如果发现得到了奇数,那么我们可以开一个暂时变量tmp=奇数 - 1(这个值可以通过奇数/2*2得到),然后tmp不断乘以2,来表示能达到的数,然后更新数组。
(vis[] 数组是用来记录1~10^5每个数被 n 个数到达需要的总步数; can[] 数组是用来记录1~10^5每个数能被 n 个数中的多少个数达到)。最后我们发现漏掉了
一开始的值 A[i] 乘以2能到达的值,所以再处理一下。
最后扫描 can[i] 数组,如果值为 n (即所有 n 个数都能到达这个数),那么用对应的 vis[i] 来更新 ans,取最小值。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; int n; int A[MAXN]; int can[MAXN]; int vis[MAXN]; int main(){ scanf("%d",&n); for(int o = 1; o <= n; ++o){ scanf("%d",&A[o]); int tA = A[o] * 2; int cnt1 = 1; while(tA < MAXN){ can[tA]++; vis[tA] += cnt1++; tA <<= 1; } tA = A[o]; cnt1 = 0; while(tA){ //deal with the current A if((tA & 1) && tA > 1){ //odd number int cnt2 = 1; //inorder to get 2 as cnt2's initial value for(int i = tA / 2 * 2; i < MAXN; i *= 2){ cnt2++; can[i]++; vis[i] += cnt1 + cnt2; } } can[tA]++; vis[tA] += cnt1++; tA >>= 1; } } //for(int i = 1; i <= 20; ++i) if(can[i] == n) printf("vis[%d] : %d\n",i,vis[i]); int ans = INF; for(int i = 1; i < MAXN; ++i) if(can[i] == n){ ans = min(ans,vis[i]); } printf("%d\n",ans); return 0; }
D题:离散化 + 统计
题意:给出一棵高度为 h (h <= 50) 的满二叉树(根的高度为1),节点编号从上到下,从左到右。现在假设某一个叶子节点(最底层节点)为这棵树的出口 exit,然后给出 q 个询问,格式是(i,l,r,ans),意思就是 exit 在第 i 层的祖先的编号是否在 [l,r] 之内,ans=1就表示“是”,ans=0就表示“否”。判断这 q 个问询是否会产生矛盾,如果不产生矛盾,那么 exit 是否唯一。
思路:先把每个询问的区间的左右端点 “推” 到最底层,对于 ans=1 的区间,我们将他们全部 “交" 起来,如果在交的过程中发现两个区间相离,那么肯定是有矛盾的。若无矛盾,最后我们得到单个 ans=1 的区间与若干个 ans=0 的区间,开一个 map,对于 ans=1 的区间我们将左端点的map值+1,(右端点+1)的map值-1;对于 ans=0 的区间将左端点的值-1,将(右端点+1)的map值+1 。然后就是一种经典的线性扫描做法,从左到右扫描每个map点,累加 map 值,如果发现当前的累加值为1,那么判断这个点到下一个map点的距离,然后用Cnt累加距离。最后,Cnt 就代表了 exit 的个数,如果 Cnt == 0 那么也是矛盾的,如果 Cnt == 1 那么输出唯一解的位置(这个可以在过程中记录),如果 Cnt > 1 那么没有唯一解。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB push_back typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; int h,q; map<ll,int> mp; map<ll,int>::iterator it,nxt_it; int main(){ ll ti,tl,tr,tans; scanf("%d%d",&h,&q); ll can_st = 1LL << (h - 1),can_ed = (1LL << h) - 1; ll tmin = 1LL << 60,tmax = 0; bool Cheat = false; for(int i = 1; i <= q; ++i){ scanf("%I64d%I64d%I64d%I64d",&ti,&tl,&tr,&tans); tl = tl << (h - ti); tr = ((tr + 1) << (h - ti)) - 1; if(tans){ //can interval if(tr < can_st || can_ed < tl) Cheat = true; else{ can_st = max(can_st,tl); can_ed = min(can_ed,tr); } } else{ //cannot interval mp[tl] -= 1; mp[tr + 1] += 1; tmin = min(tmin,tl); tmax = max(tmax,tr); } } if(Cheat){ printf("Game cheated!\n"); return 0; } ll sta = 0,Cnt = 0,ans_pos; mp[can_st] += 1; mp[can_ed + 1] -= 1; for(it = mp.begin(); it != mp.end(); it++){ sta += (*it).second; if(sta == 1){ nxt_it = it; nxt_it++; if(nxt_it == mp.end()) continue; ll cur = (*nxt_it).first - (*it).first; Cnt += cur; if(cur == 1) ans_pos = (*it).first; } } if(Cnt == 0) printf("Game cheated!\n"); else if(Cnt == 1) printf("%I64d\n",ans_pos); else printf("Data not sufficient!\n"); return 0; }
E题:线段树(多棵)
题意:给出一个长度为 n 的字符串(n <= 10^5),q(q <= 50000)个操作,每个操作的形式:(i,j,k),如果 k=0 那么将区间 [i,j] 内的字母降序排序,如果 k=1 那么将区间 [i,j] 内的字母升序排序。输出进行完所有操作的字符串。限时:5秒。
思路:既然限时这么宽松... 不妨直接上数据结构。看到有用 set、map 的。
我的思路是开26棵线段树,来记录每个字母在字符串中的位置。因为排序后相同的字母是排列在一起的,很有规律,可以最多进行26次区间修改来实现操作。其他没什么技巧的,纯代码题。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; int n,q; char s[MAXN]; int tsum[27][MAXN << 2],flag[27][MAXN << 2]; int Len[MAXN << 2]; char ans[MAXN]; void Push_down(int id,int p){ if(flag[id][p] != -1){ flag[id][p << 1] = flag[id][p]; flag[id][p << 1|1] = flag[id][p]; if(flag[id][p] == 1){ tsum[id][p << 1] = Len[p << 1]; tsum[id][p << 1|1] = Len[p << 1|1]; } else if(flag[id][p] == 0){ tsum[id][p << 1] = tsum[id][p << 1|1] = 0; } flag[id][p] = -1; } } void Update(int id,int a,int b,int c,int p,int l,int r){ if(a <= l && r <= b){ flag[id][p] = c; if(flag[id][p] == 1) tsum[id][p] = Len[p]; else if(flag[id][p] == 0) tsum[id][p] = 0; return; } Push_down(id,p); int mid = getmid(l,r); if(a <= mid) Update(id,a,b,c,p << 1,l,mid); if(b > mid) Update(id,a,b,c,p << 1|1,mid + 1,r); tsum[id][p] = tsum[id][p << 1] + tsum[id][p << 1|1]; } int Query(int id,int a,int b,int p,int l,int r){ if(a <= l && r <= b){ return tsum[id][p]; } Push_down(id,p); int mid = getmid(l,r); int res = 0; if(a <= mid) res += Query(id,a,b,p << 1,l,mid); if(b > mid) res += Query(id,a,b,p << 1|1,mid + 1,r); return res; } void Get(int p,int l,int r){ Len[p] = r - l + 1; if(l == r) return; int mid = getmid(l,r); Get(p << 1,l,mid); Get(p << 1|1,mid + 1,r); } void Print(int id,int p,int l,int r){ if(tsum[id][p] == 0) return; if(l == r){ ans[l] = 'a' + id; return; } Push_down(id,p); int mid = getmid(l,r); Print(id,p << 1,l,mid); Print(id,p << 1|1,mid + 1,r); } int main(){ memset(flag,-1,sizeof(flag)); int a,b,k; scanf("%d%d",&n,&q); Get(1,1,n); scanf("%s",s + 1); for(int i = 1; i <= n; ++i){ int id = s[i] - 'a'; Update(id,i,i,1,1,1,n); } int cnt[30]; for(int o = 1; o <= q; ++o){ scanf("%d%d%d",&a,&b,&k); for(int i = 0; i < 26; ++i){ cnt[i] = Query(i,a,b,1,1,n); if(cnt[i]){ Update(i,a,b,0,1,1,n); } } int st = a; if(k == 1){ for(int i = 0; i < 26; ++i) if(cnt[i]){ Update(i,st,st + cnt[i] - 1,1,1,1,n); st += cnt[i]; } } else{ for(int i = 25; i >= 0; --i) if(cnt[i]){ Update(i,st,st + cnt[i] - 1,1,1,1,n); st += cnt[i]; } } } for(int i = 0; i < 26; ++i){ Print(i,1,1,n); } ans[n + 1] = '\0'; printf("%s\n",ans + 1); return 0; }