看了我的游记的都知道我每道题目的做题情况。
T1
比较简单的签到题目。
你先贪心选每个数最大的那个组,然后判断有没有某一个组的大于 \(\frac{n}{2}\),显然这种组有且仅有一个。
我们考虑把这个组的某些数换组,那么到 \(frac{n}{2}\) 就够了。
显然换到那个数的第二大的那个组即可,相减一下就是代价,直接用大根堆维护即可。
时间复杂度 \(\mathcal{O}(Tn\log n)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <queue>
#define int long long
#define N 100005
using namespace std;
priority_queue<int> q[5];
int n;
struct node{int val,id;
}a[N][5];
signed main(){int T;cin >> T;for (;T--;) {for (int j = 1;j <= 3;j ++) {while(!q[j].empty()) q[j].pop();}scanf("%lld",&n);for (int i = 1;i <= n;i ++)for (int j = 1;j <= 3;j ++) scanf("%lld",&a[i][j].val),a[i][j].id = j;int ans = 0;for (int i = 1;i <= n;i ++) {stable_sort(a[i] + 1,a[i] + 1 + 3,[](node x,node y) {return x.val > y.val;});ans += a[i][1].val;q[a[i][1].id].push(a[i][2].val - a[i][1].val);}// cout << ans << '\n';bool flag = 0;for (int i = 1;i <= 3;i ++)flag |= (q[i].size() > n / 2);if (!flag) {printf("%lld\n",ans);continue;}int mxid = 1;for (int i = 2;i <= 3;i ++)if (q[i].size() > n / 2) {mxid = i;break;}// cout << mxid << '\n';while(q[mxid].size() > n / 2) {int t = q[mxid].top();ans += t;q[mxid].pop();}printf("%lld\n",ans);}system("pause");return 0;
}
T2
并未做出来,原因:误认为 \(2^k\leq 1.5\times 10^6\),就是 \(2^{20}\) 的那个上限,然后就不会了。
但其实显然 \(m\) 是诈骗,压成 \(n-1\) 条边就行了,于是可以得到本题性价比极高的 \(80pts\) 做法:\(\mathcal{O}(2^k(nk)\log (nk))\),感觉 \(CCF\) 现在换成 \(\text{Cure Ultra9}\) 可能能过。
然后你把排序换到外面去就能通过本题了。
再说一说我考场上的思路为什么错:每个城镇贡献一个完全图,这样的连边方式是错误的,因为会多算很多次 \(c\)(但是只没有通过一个大样例就很奇妙)。
T3
赛场上应该是可以切掉的,但是由于 \(T_2\) 没有通过大样例的心理施压导致没有把心思放到这上面来。
CCF 的题面提示我们可以将一个替换操作和被替换询问变成:前缀相同+第一个字符串不同的+第二个字符串不同的+后缀相同的,中间用空格隔开。
我们关心的只是不同的情况,对于前缀和后缀我们只需要判断是不是当前询问的前缀相同的后缀且是其后缀相同的前缀(可能有一点绕,请细细品位),然后肯定可以用 trie树 或者 AC自动机 维护,但是我想先验证一下,打了一个hash。
打了很久,这是交在洛谷只有 \(10pts\) 的赛场复刻代码,不忍直视:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <map>
#define int long long
#define uint unsigned long long
#define N 200005
#define M 5500005
#define PII pair<int,int>
using namespace std;
int n,q;
uint Pow[M],seed = 231;
map<uint,int> mp;
map<uint,vector<PII> >mp2;
signed main(){Pow[0] = 1;for (int i = 1;i < M;i ++) Pow[i] = Pow[i - 1] * seed;cin >> n >> q;for (int i = 1;i <= n;i ++) {string s1,s2;cin >> s1 >> s2;int st = 0;while(s1[st] == s2[st]) st ++;int ed = s1.size() - 1;while(s1[ed] == s2[ed]) ed --;uint h = 0,hh = 0;for (int i = 0;i < st;i ++) h = h * seed + s1[i];h = h * seed + ' ';for (int i = st;i <= ed;i ++) h = h * seed + s1[i],hh = hh * seed + s1[i];h = h * seed + ' ';hh = hh * seed + ' ';for (int i = st;i <= ed;i ++) h = h * seed + s2[i],hh = hh * seed + s2[i];h = h * seed + ' ';for (int i = ed + 1;i < s1.size();i ++) h = h * seed + s1[i];mp[h] ++;mp2[hh].push_back({st,(int)s1.size() - ed - 1});// cout << h << ' ' << hh << '\n';}for (string t1,t2;q --;) {cin >> t1 >> t2;if (t1.size() != t2.size()) {puts("0");continue;}int st = 0;while(t1[st] == t2[st]) st ++;int ed = t1.size() - 1;while(t1[ed] == t2[ed]) ed --;string s = "";for (int i = 0;i < st;i ++) s += t1[i];s += " ";uint h2 = 0;for (int i = st;i <= ed;i ++) s += t1[i],h2 = h2 * seed + t1[i];s += " ";h2 = h2 * seed + ' ';for (int i = st;i <= ed;i ++) s += t2[i],h2 = h2 * seed + t2[i];s += " ";for (int i = ed + 1;i < t1.size();i ++) s += t1[i];vector<uint> hh;uint h = 0;for (auto i : s) h = h * seed + i,hh.push_back(h);int ans = 0;for (auto i : mp2[h2]) {int l = st - i.first,r = st + (2 * (ed - st + 2)) + i.second;// cout << i.first << '-' << i.second << '\n';if (l < 0 || r > s.size()) continue;// cout << l << ' ' << r << '\n';uint hsh = 0;// for (int j = l;j <= r;j ++) hsh = hsh * seed + s[j];if (l == 0) hsh = hh[r];else hsh = hh[r] - hh[l - 1] * Pow[r - l + 1];// cout << hsh << '\n';ans += mp[hsh];}printf("%lld\n",ans);}// system("pause");return 0;
}
样例 \(3\) 是通过不了的,输出的比他多。
你看出来是哪里多了吗?
没错!
由于每一对长度可能相同,但是方案只有这种类型的串的个数,因此我们去重一下就行了,就变成了:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <map>
#define int long long
#define uint unsigned long long
#define N 200005
#define M 5500005
#define PII pair<int,int>
using namespace std;
int n,q;
uint Pow[M],seed = 231;
map<uint,int> mp;
map<uint,vector<PII> >mp2;
signed main(){Pow[0] = 1;for (int i = 1;i < M;i ++) Pow[i] = Pow[i - 1] * seed;cin >> n >> q;for (int i = 1;i <= n;i ++) {string s1,s2;cin >> s1 >> s2;int st = 0;while(s1[st] == s2[st]) st ++;int ed = s1.size() - 1;while(s1[ed] == s2[ed]) ed --;uint h = 0,hh = 0;for (int i = 0;i < st;i ++) h = h * seed + s1[i];h = h * seed + ' ';for (int i = st;i <= ed;i ++) h = h * seed + s1[i],hh = hh * seed + s1[i];h = h * seed + ' ';hh = hh * seed + ' ';for (int i = st;i <= ed;i ++) h = h * seed + s2[i],hh = hh * seed + s2[i];h = h * seed + ' ';for (int i = ed + 1;i < s1.size();i ++) h = h * seed + s1[i];mp[h] ++;mp2[hh].push_back({st,(int)s1.size() - ed - 1});// cout << h << ' ' << hh << '\n';}for (auto i : mp2) {//多了这一段代码sort(i.second.begin(),i.second.end());i.second.erase(unique(i.second.begin(),i.second.end()),i.second.end());mp2[i.first] = i.second;//注意这个,赛场没有写这个所以没有减枝优化}for (string t1,t2;q --;) {cin >> t1 >> t2;if (t1.size() != t2.size()) {puts("0");continue;}int st = 0;while(t1[st] == t2[st]) st ++;int ed = t1.size() - 1;while(t1[ed] == t2[ed]) ed --;string s = "";for (int i = 0;i < st;i ++) s += t1[i];s += " ";uint h2 = 0;for (int i = st;i <= ed;i ++) s += t1[i],h2 = h2 * seed + t1[i];s += " ";h2 = h2 * seed + ' ';for (int i = st;i <= ed;i ++) s += t2[i],h2 = h2 * seed + t2[i];s += " ";for (int i = ed + 1;i < t1.size();i ++) s += t1[i];vector<uint> hh;uint h = 0;for (auto i : s) h = h * seed + i,hh.push_back(h);int ans = 0;for (auto i : mp2[h2]) {int l = st - i.first,r = st + (2 * (ed - st + 2)) + i.second;if (l < 0) break;//排序后的减枝// cout << i.first << '-' << i.second << '\n';if (l < 0 || r >= s.size()) continue;// cout << l << ' ' << r << '\n';uint hsh = 0;// for (int j = l;j <= r;j ++) hsh = hsh * seed + s[j];if (l == 0) hsh = hh[r];else hsh = hh[r] - hh[l - 1] * Pow[r - l + 1];// cout << hsh << '\n';ans += mp[hsh];}printf("%lld\n",ans);}// system("pause");return 0;
}
感觉这个要卡还是能卡的(要卡常),不过在随机数据下面是比较好的,如果刻意去卡的话就是构造一个类似:
b c
? b c ?
?? b c ??
....
询问就是都询问 \(b\) 换成 \(c\) 的方案嘛,由于我们注意到其替换方案的字符串总和 \(\leq 5\times 10^6\),而上面那个是等差数列求和,而且绝对不满 \(\sqrt\sum\),差不多是 \(\mathcal{O}(\sum|s_i|+\sum|t_i|+q\sqrt{\sum|s_i|})\)。
比较极限,但是应该可以过。
T4
目前不会。