开个新号打打codeforces(以前那号玩废了),结果就遇到了这么难一套。touristD题用了map,被卡掉了(其实是对cf的评测机过分自信),G题没过, 700多行代码,码力惊人。关键是这次tourist掉到第二了,掉了200多分,为神节哀。
做了4道,要不是第4题一直炸第5题也能做出来。本来想着上蓝名的。然后我第二题挂了,判断循环节写错了。绝望啊~~~~
比赛传送门:http://codeforces.com/contest/1043
A. Elections
这题很简单,就是说有n个人,每人投k张票,给你或你对手。第i个人会给你的对手投ai票,让你求最少的k。使得你的票数比你的对手多。模拟一下就好了,不多说。然而这题我又犯傻忘记了k >= max{ai},wa了一次。
弱弱地放上我的代码
1 #include2 #include 3 #include 4 #include 5 #define rep(x, l, r) for(int x = l; x <= r; x++) 6 #define repd(x, r, l) for(int x = r; x >= l; x--) 7 #define clr(x,y) memset(x, y, sizeof(x)) 8 #define mp(x, y) make_pair(x, y) 9 #define INF 1 << 30 10 #define MAXN 11 using namespace std; 12 typedef long long LL; 13 typedef pair<int,int> par; 14 15 int main(){ 16 int n; 17 scanf("%d", &n); 18 int sum = 0, maxx = 0; 19 rep(i, 1, n){ 20 int x; 21 scanf("%d", &x); 22 sum += x; 23 maxx = max(maxx, x); 24 } 25 int ans = 2 * sum / n + 1; 26 if(ans < maxx) ans = maxx; 27 printf("%d\n", ans); 28 return 0; 29 }
B. Lost Array
这题是给你个数组a,长度为n,满足(k是x的长度)。当k = 3, x = {1, 2, 3}, n = 5时a如下图。
让你求所有x数组的方案数和,以及每种方案的长度k(从大到小)。
这题看上去很麻烦,其实很简单。只要求出他们相邻的差,就是一种x的方案。另外的其他几个方案就是由这串差的循环节组成(为什么?自己试试就知道了)。
然而我找循环节炸了,我也不知道为啥,主要还是代码太丑。后来听了大佬的建议,才改进了一点。
改过后的代码
1 #include2 #include 3 #include 4 #include 5 #define rep(x, l, r) for(int x = (int)l; x <= (int)r; x++) 6 #define repd(x, r, l) for(int x = (int)r; x >= (int)l; x--) 7 #define clr(x,y) memset(x, y, sizeof(x)) 8 #define mp(x, y) make_pair(x, y) 9 #define INF 1 << 30 10 #define MAXN 1005 11 using namespace std; 12 typedef long long LL; 13 typedef pair<int,int> par; 14 15 int n; 16 int a[MAXN], b[MAXN], ansk[MAXN]; 17 18 bool judge(int len){ 19 rep(i, len, n - 1) 20 if(a[i] != a[i % len]) return 0; 21 return 1; 22 } 23 24 int main(){ 25 scanf("%d", &n); 26 rep(i, 1, n){ 27 scanf("%d", &b[i]); 28 a[i - 1] = b[i] - b[i - 1]; 29 } 30 int ans = 0; 31 rep(i, 1, n) 32 if(judge(i)) ansk[++ans] = i; 33 printf("%d\n", ans); 34 rep(i, 1, ans) printf("%d ", ansk[i]); 35 puts(""); 36 return 0; 37 }
C. Smallest Word
这题大意就是给你一个字符串s(只由a和b组成),你每次可以将这个字符串的任意前缀翻转,让你求得到字典序最小的字符串的任意一种操作。
因为这个字符串只有ab,所以这题最后得到的字符串一定前面都是a,后面都是b(这是一定能得到的,至于为什么,我说不清),所以对于每次翻转,都要把所有的a放一起,b放一起。
然后我们开始模拟一下,随便写个字符串ababbaaababaaab。(懒得画图)
我们的算法就是把所有a和b分开放。
这是原先的字符串ab|abbaaababaaab
旋转ab 旋转baa 旋转aabbb 旋转bbbaaaaa 旋转aaaaabbbb 旋转bbbbaaaaaa 旋转aaaaaabbbbb 旋转bbbbbaaaaaaaaa |
变成baa|bbaaababaaab 变成aabbb|aaababaaab 变成bbbaaaaa|babaaab 变成aaaaabbbb|abaaab 变成bbbbaaaaaa|baaab 变成aaaaaabbbbb|aaab 变成bbbbbaaaaaaaaa|b 变成aaaaaaaaabbbbbb |
中间的竖杠之前的就是已经将a和b区分开来的字符串,然后我们发现,竖杠之前就是要旋转的地方(为了使a或b连在一起)。然后进一步发现旋转的地方就是a和b的交界线(why?no why!)。
所以我们只要找到所有a和b的交界线就好了,还有就是如果最后一位是a就要在最后将整个字符串旋转(我居然因为这个wa了一次……
代码如下
1 #include2 #include 3 #include 4 #include 5 #define rep(x, l, r) for(int x = (int)l; x <= (int)r; x++) 6 #define repd(x, r, l) for(int x = (int)r; x >= (int)l; x--) 7 #define clr(x,y) memset(x, y, sizeof(x)) 8 #define mp(x, y) make_pair(x, y) 9 #define INF 1 << 30 10 #define MAXN 1005 11 using namespace std; 12 typedef long long LL; 13 typedef pair<int,int> par; 14 15 char st[MAXN]; 16 bool flag[MAXN]; 17 18 int main(){ 19 scanf("%s", st + 1); 20 int len = strlen(st + 1); 21 rep(i, 1, len - 1) 22 if(st[i] != st[i + 1]) flag[i] = 1; 23 if(st[len] == 'a') flag[len] = 1; 24 rep(i, 1, len) printf("%d ", flag[i]); 25 puts(""); 26 return 0; 27 }
D. Mysterious Crime
这题是真的坑,一开始压根没思路,辛亏后来开窍了。
这题是说给你一个m*n(1≤n≤100000, 1≤m≤10)的矩阵,每行都是一个[1,n]的排列。让你每行删掉任意长度的前缀和后缀(也可以不删),使得每行剩下的序列相等。问你方案总数。其实就是有多少个序列是每行都拥有的。(然而我一开始还读了半天题)
然而我一开始读错数据,把n和m的大小读反了,main包似乎也是。然我我一开始想到了暴力+kmp,main想到了搜索。然后我就wa了一次,居然给过样例了,不可思议。
听dalao说好像是什么后缀数组(听都没听说过),然后就想到了一个很……的代码。似乎没几个人和我代码思路一样,除了tourist(然后他T了,掉下了rating榜第一名)
因为每行n个数各自只出现一次,所以说只要用s[a[1][j]].nxt记录第一行每个数字a[1][j]的下一个数字a[1][j + 1],一个长为l序列s是每行都拥有仅当每行的s[i]都的下一个元素为s[i + 1](1 <= i < l)。所以用s[a[1][j]].cnt记录下有多少行中a[1][j]的下一个为a[1][j + 1]即s[a[1][j]].nxt。
然后我们找到所有cnt为m - 1即所有行都出现过的相邻的数,但是如果A->B和B->C在后m-1行都出现,那么A,B,C也是一个满足每行拥有的序列。所以我们还要记录所有连续的串的长度(如A->B->C都满足就为3),答案为所有长度*(长度 + 1)/ 2的总和(为什么?这不是小学数学吗。)
还有一个单独的点长度算1,因为单独一个数也算是一个序列,且每行都有。
不过回过头来一看,上面这段话说了什么自己也看不懂,算了凑合凑合,看下代码吧:
1 #include2 #include 3 #include 4 #include 5 #define rep(x, l, r) for(int x = (int)l; x <= (int)r; x++) 6 #define repd(x, r, l) for(int x = (int)r; x >= (int)l; x--) 7 #define clr(x,y) memset(x, y, sizeof(x)) 8 #define mp(x, y) make_pair(x, y) 9 #define INF 1 << 30 10 #define MAXN 15 11 #define MAXM 100005 12 using namespace std; 13 typedef long long LL; 14 typedef pair<int,int> par; 15 16 struct node{ 17 int nxt; 18 LL cnt; 19 }s[MAXM]; 20 int a[MAXN][MAXM]; 21 22 int main(){ 23 int n, m; 24 scanf("%d%d", &n, &m); 25 rep(i, 1, m) 26 rep(j, 1, n) scanf("%d", &a[i][j]); 27 rep(i, 1, n - 1){ 28 s[a[1][i]].nxt = a[1][i + 1]; 29 s[a[1][i]].cnt = 1; 30 } 31 rep(i, 2, m) 32 rep(j, 1, n - 1) 33 if(s[a[i][j]].nxt == a[i][j + 1]) s[a[i][j]].cnt++; 34 LL sum = 1, ans = 0; 35 rep(i, 1, n - 1){ 36 if(s[a[1][i]].cnt != m) ans += sum * (sum + 1) / 2, sum = 1; 37 else sum++; 38 } 39 if(sum > 0) ans += sum * (sum + 1) / 2; 40 cout << ans << endl; 41 return 0; 42 }
E. Train Hard, Win Easy
上一次看错题了,搞了半天样例打错,之前的题解是错的,抱歉,现在改了。
这题时说有n个大佬去打比赛(就两题),第i个大佬第一题会得a[i]分,第二题会得b[i]分,现在每次选俩大佬打模拟赛,每道题各选一个大佬,使获得的总分最少(为啥最少,我也不知道)。然后有m对大佬不会一起打。让你求每个大佬每次比赛中团队的总分。
当i和j一起打,
假设i做第一道
所以说a[i] + b[j] < a[j] + b[i]
那么a[i] - b[i] < a[i] - b[j]
所以说我们只要将a[i] - b[i]排序,如果j在i的前面,那么第i个人就要做一次第二题,否则做一次第一题。
我们用id[i]记录排序后i所在的位置,那么i就要做id[i] - 1次第二题,n - id次第一道,另外因为记录每次比赛团队总分,所以答案还得加上a[1] ~ a[id[i] - 1]和b[id[i] + 1] ~ b[n]。所以还需要开个前缀和和后缀和。
再判断不会一起打的人在他的前面还是后面,减去。
时间复杂度为O(n + m)。
代码如下
1 #include2 #include 3 #include 4 #include 5 #define rep(x, l, r) for(int x = (int)l; x <= (int)r; x++) 6 #define repd(x, r, l) for(int x = (int)r; x >= (int)l; x--) 7 #define clr(x,y) memset(x, y, sizeof(x)) 8 #define mp(x, y) make_pair(x, y) 9 #define all(x) begin(x), end(x) 10 #define MAXN 500005 11 #define fi first 12 #define se second 13 #define Size(x) ((int)size(x)) 14 using namespace std; 15 typedef long long LL; 16 typedef vector<int> vi; 17 typedef pair int> pli; 18 typedef pair pll; 19 const int INF = 1 << 30; 20 const int p = 10000007; 21 //by DYH 22 23 pli c[MAXN]; 24 pll sum[MAXN]; 25 int id[MAXN]; 26 LL a[MAXN], b[MAXN], ans[MAXN]; 27 28 int main(){ 29 int n, m; 30 scanf("%d%d", &n, &m); 31 rep(i, 1, n){ 32 scanf("%I64d%I64d", &a[i], &b[i]); 33 c[i] = mp(a[i] - b[i], i); 34 } 35 sort(c + 1, c + n + 1); 36 rep(i, 1, n) id[c[i].se] = i; 37 sum[0] = mp(0, 0); 38 sum[n + 1] = mp(0, 0); 39 rep(i, 1, n) sum[i].fi = sum[i - 1].fi + a[c[i].se]; 40 repd(i, n, 1) sum[i].se = sum[i + 1].se + b[c[i].se]; 41 rep(i, 1, n) ans[i] = a[i] * (n - id[i]) + b[i] * (id[i] - 1) + sum[id[i] - 1].fi + sum[id[i] + 1].se; 42 rep(times, 1, m){ 43 int x, y; 44 scanf("%d%d", &x, &y); 45 if(id[x] < id[y]) ans[x] -= a[x] + b[y], ans[y] -= a[x] + b[y]; 46 else ans[x] -= a[y] + b[x], ans[y] -= a[y] + b[x]; 47 } 48 rep(i, 1, n) printf("%I64d ", ans[i]); 49 puts(""); 50 return 0; 51 }
终于打完啦!2018-10-31 15:58:34
这是tourist的TLE代码,CF的c++11导致tourist掉了200分并且掉下rating榜第一的代码。
1 /** 2 * author: tourist 3 * created: 28.10.2018 18:47:36 4 **/ 5 #include6 7 using namespace std; 8 9 int main() { 10 ios::sync_with_stdio(false); 11 cin.tie(0); 12 int n, m; 13 cin >> n >> m; 14 map int,int>,int> mp; 15 for (int i = 0; i < m; i++) { 16 int foo; 17 cin >> foo; 18 foo--; 19 for (int j = 1; j < n; j++) { 20 int bar; 21 cin >> bar; 22 bar--; 23 mp[make_pair(foo, bar)]++; 24 foo = bar; 25 } 26 } 27 vector<int> to(n, -1), from(n, -1); 28 for (auto &p : mp) { 29 if (p.second == m) { 30 to[p.first.first] = p.first.second; 31 from[p.first.second] = p.first.first; 32 } 33 } 34 long long ans = 0; 35 for (int i = 0; i < n; i++) { 36 if (from[i] == -1) { 37 int len = 1; 38 int x = i; 39 while (to[x] != -1) { 40 x = to[x]; 41 len++; 42 } 43 ans += (long long) len * (len + 1) / 2; 44 } 45 } 46 cout << ans << '\n'; 47 return 0; 48 }
摆上tourist怒刷一波评测证明了以后我们cf要交C++14不要交C++11(C++19:???)