A Sereja and Algorithm
题意:给定有x,y,z组成的字符串,每次询问某一段s[l, r]能否变成变成zyxzyx的循环体。
分析:
分析每一段x,y,z数目是否满足构成循环体,当然长度<3的要特判。
代码:
1 #include <bits/stdc++.h> 2 #define in freopen("solve_in.txt", "r", stdin); 3 #define pb push_back 4 5 using namespace std; 6 typedef long long LL; 7 8 const int maxn = (int)1e5 + 100; 9 char s[maxn]; 10 int cnt[maxn][3]; 11 int main() { 12 13 int m; 14 scanf("%s", s+1); 15 scanf("%d", &m); 16 int len = strlen(s+1); 17 for(int i = 1; i <= len; i++) { 18 for(int k = 0; k < 3; k++) 19 cnt[i][k] = cnt[i-1][k]; 20 cnt[i][s[i]-‘x‘]++; 21 } 22 for(int i = 0; i < m; i++) { 23 int l, r; 24 scanf("%d%d", &l, &r); 25 int a = cnt[r][0]-cnt[l-1][0]; 26 int b = cnt[r][1]-cnt[l-1][1]; 27 int c = cnt[r][2]-cnt[l-1][2]; 28 int tmp = r-l+1; 29 if(tmp < 3) 30 puts("YES"); 31 else { 32 33 if(tmp%3 == 0) { 34 if(a == b && b == c) 35 puts("YES"); 36 else puts("NO"); 37 } else if(tmp%3 == 1) { 38 if((a == b && c - b == 1) || (b == c && a-b == 1) || (a == c &&b -a == 1)) 39 puts("YES"); 40 else puts("NO"); 41 } else { 42 if((a == b && a-c == 1) || (b == c && b-a==1) || (a == c && a-b == 1)) 43 puts("YES"); 44 else puts("NO"); 45 } 46 } 47 } 48 return 0; 49 }
B Sereja ans Anagrams
题意:给定2个数组,a[1...n],以及b[1...m]问能否从a中等间隔的选取m个数,使得这m个数与b中各个整数数目对应相等。
分析:
容易知道由于是等间隔p的出现,所以可以将a[1..n]的数按间隔分成p组,每次添加一个数时,看相应的组m个数形成的序列是否和b中数的数目对应相等,每次添加,减少一个数,最多改变2个数的数目,看是不是所有的数的数目都满足与在b中的相等。感觉和单调队列类似。
代码:
1 #include <bits/stdc++.h> 2 #define in freopen("solve_in.txt", "r", stdin); 3 #define pb push_back 4 5 using namespace std; 6 typedef long long LL; 7 typedef map<int, int> MPII; 8 9 const int maxn = 2*(int)1e5; 10 int n, m, p; 11 MPII mps, mx[maxn]; 12 vector<int> ans; 13 int a[maxn], b[maxn]; 14 int sat[maxn]; 15 queue<int> q[maxn]; 16 17 int main() { 18 19 scanf("%d%d%d", &n, &m, &p); 20 for(int i = 0; i < n; i++) { 21 scanf("%d", a+i); 22 } 23 int dif = 0; 24 for(int i = 0; i < m; i++) { 25 int t; 26 scanf("%d", &t); 27 if(mps[t] == 0) 28 dif++; 29 mps[t]++; 30 } 31 for(int i = 0; i < n; i++) { 32 int di = i%p; 33 if(q[di].size() >= m) { 34 int u = q[di].front(); 35 q[di].pop(); 36 if(mx[di][u] == mps[u]) 37 sat[di]--; 38 else if(mx[di][u] == mps[u]+1) 39 sat[di]++; 40 mx[di][u]--; 41 } 42 int u = a[i]; 43 q[di].push(a[i]); 44 if(mx[di][u] == mps[u]) 45 sat[di]--; 46 else if(mx[di][u] == mps[u]-1) 47 sat[di]++; 48 mx[di][u]++; 49 if(sat[di] == dif) 50 ans.pb(i-(m-1)*p); 51 } 52 cout<<ans.size()<<endl; 53 for(int i = 0; i < ans.size(); i++) { 54 printf("%d%c", ans[i]+1, i == ans.size()-1?‘\n‘:‘ ‘); 55 } 56 return 0; 57 }
C Sereja and the Arrangement of Numbers
题意:n个位置,从m不同的数中选一些数去填充,构成一个数组,数组中出现的任何两个数一定要在数组中至少连续出现一次,每个数都有一个代价,要求选出的数总代价最大!
分析:首先肯定的是选出代价尽量大的数,那么就要确定n个位置最多放多少个不同的数,做的时候其实是想到了完全图的,但是没继续想下去。由于任何两个数都要相邻出现,所以肯定是满足完全图关系的,但是如果将相邻关系看做一条边的话,n个位置n-1条边,而且这n-1条边是能够从左走到右,也就是构成一个半欧拉图。
半欧拉图:最多有两个点度数为奇数。对于偶数个节点完全图,每个点度数为奇数,添加一条边能够使得两个点度数变为偶数,所以最少需要添加(点数/2-1)条边构成半欧拉图,因为最终还是允许两个点度数为奇数。奇数个结点完全图,度数为偶数,所以不需要添加边了。
这样根据,图中n-1条边二分最多能放置的结点数,得到数组中最多出现多少个不同的数,贪心选取最大就行了。
代码:
1 #include <bits/stdc++.h> 2 #define in freopen("solve_in.txt", "r", stdin); 3 #define pb push_back 4 5 using namespace std; 6 typedef long long LL; 7 typedef map<int, int> MPII; 8 const int maxn = (int)1e5 + 100; 9 10 int w[maxn]; 11 int getAns(int lim, int r){ 12 int l = 1; 13 while(l < r){ 14 int mid((l+r+1)>>1); 15 int tmp; 16 if(mid&1){ 17 tmp = ((LL)mid*(mid-1)>>1); 18 if(tmp <= lim) 19 l = mid; 20 else r = mid-1; 21 } 22 else { 23 tmp = ((LL)mid*(mid-1)/2+mid/2-1); 24 if(tmp <= lim) 25 l = mid; 26 else r = mid-1; 27 } 28 } 29 return l; 30 } 31 int main(){ 32 33 int n, m; 34 scanf("%d%d", &n, &m); 35 for(int i(0); i != m; i++){ 36 scanf("%*d%d", w+i); 37 } 38 sort(w, w+m, greater<int> ()); 39 n = getAns(n-1, m); 40 LL ans = 0; 41 for(int i = 0; i != n; i++) 42 ans += w[i]; 43 cout<<ans<<endl; 44 return 0; 45 }