Codeforces Goodbye 2021 前五题题解
明天就解除隔离了,今天没啥事儿就来看看,感觉不想干事,那就看看blog吧
A题,机器人有四个方向,碰到墙壁会发生折射,入射角等于出射角,问什么时候碰到目标格子
以为是什么结论题,结果很简单,尬膜就行了,试了一发就AC了
#include <bits/stdc++.h> using namespace std; #define limit (500500 + 5)//防止溢出 #define INF 0x3f3f3f3f #define inf 0x3f3f3f3f3f #define lowbit(i) i&(-i)//一步两步 #define EPS 1e-9 #define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0); #define ff(a) printf("%d\n",a ); #define pi(a,b) pair<a,b> #define rep(i, a, b) for(ll i = a; i <= b ; ++i) #define per(i, a, b) for(ll i = b ; i >= a ; --i) #define MOD 998244353 #define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next) #define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin) #define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout) typedef long long ll; typedef unsigned long long ull; char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; inline ll read(){ #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) ll sign = 1, x = 0;char s = getchar(); while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();} while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();} return x * sign; #undef getchar }//快读 void print(ll x) { if(x/ 10) print(x / 10); *O++=x % 10+'0'; } void write(ll x, char c = 't') { if(x < 0)putchar('-'),x = -x; print(x); if(!isalpha(c))*O++ = c; fwrite(obuf,O-obuf,1,stdout); O = obuf; } int kase; const ll mod = MOD; ll quickPow(ll base, ll expo){ ll ans = 1; while (expo){ if(expo & 1)(ans *= base) %= mod; expo >>= 1; base = base * base; base %= mod; } return ans % mod; } ll C(ll n, ll m){ if(n < m)return 0; ll x = 1, y = 1; if(m > n - m)m = n - m; rep(i ,0 , m - 1){ x = x * (n - i) % mod; y = y * (i + 1) % mod; } return x * quickPow(y, mod - 2) % mod; } ll lucas(ll n, ll m){ return !m || n == m ? 1 : C(n % mod, m % mod) * lucas(n / mod, m / mod) % mod; } int prime[limit],tot,num[limit],phi[limit],miu[limit]; void get_prime(const int &n = 1e6){ memset(num,1,sizeof(num)); num[1] = num[0] = 0; miu[1] = 1; rep(i,2,n){ if(num[i])prime[++tot] = i,miu[i] = -1,phi[i] = i - 1; for(int j = 1; j <= tot && prime[j] * i <= n ; ++j){ num[prime[j] * i] = 0; if(i % prime[j] == 0){ miu[i * prime[j]] = 0; break; }else{ miu[i * prime[j]] = -miu[i];//莫比乌斯函数 } } } }//素数筛 int n, m,k; int a[limit]; int b[limit]; decltype(views::iota(1,2)) iter(int x, int y) { return views::iota(x, y + 1); } void solve(){ cin>>n; map<int, int>pos,neg; int ans = 0; for(auto i:iter(1,n)){ cin>>a[i]; a[i] = abs(a[i]); if(a[i] > 0){ if(!pos[a[i]]){ pos[a[i]]++; ++ans; }else{ if(!neg[a[i]]){ neg[a[i]]++; ++ans; } } }else if(a[i] < 0){ if(!neg[a[i]]){ neg[a[i]]++; ++ans; }else{ if(!pos[a[i]]){ pos[a[i]]++; ++ans; } } }else if(a[i] == 0){ if(!pos[a[i]]){ pos[a[i]]++; ans++; } } } cout<<ans<<endl; } int32_t main() { #ifdef LOCAL FOPEN; // FOUT; #endif FASTIO cin>>kase; // get_prime(3e5); while (kase--) solve(); cerr << "Time elapsed: " << 1.0*clock()/CLOCKS_PER_SEC << "s\n"; return 0; }
B题,给一个字符串,要求求一个前缀使得前缀反转拼接的回文串字典序最小
不难想出来我们贪心地选取最长的严格下降序列作为前缀,但有个坑点就是结尾可能会出现多个相同最小字符相邻的情况,比如cba作为目标答案不会比cbaa更优,没什么难的,wa了一发之后很快过掉了
#include <bits/stdc++.h> using namespace std; #define limit (500500 + 5)//防止溢出 #define INF 0x3f3f3f3f #define inf 0x3f3f3f3f3f #define lowbit(i) i&(-i)//一步两步 #define EPS 1e-9 #define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0); #define ff(a) printf("%d\n",a ); #define pi(a,b) pair<a,b> #define rep(i, a, b) for(ll i = a; i <= b ; ++i) #define per(i, a, b) for(ll i = b ; i >= a ; --i) #define MOD 998244353 #define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next) #define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin) #define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout) typedef long long ll; typedef unsigned long long ull; char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; inline ll read(){ #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) ll sign = 1, x = 0;char s = getchar(); while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();} while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();} return x * sign; #undef getchar }//快读 void print(ll x) { if(x/ 10) print(x / 10); *O++=x % 10+'0'; } void write(ll x, char c = 't') { if(x < 0)putchar('-'),x = -x; print(x); if(!isalpha(c))*O++ = c; fwrite(obuf,O-obuf,1,stdout); O = obuf; } int kase; const ll mod = MOD; ll quickPow(ll base, ll expo){ ll ans = 1; while (expo){ if(expo & 1)(ans *= base) %= mod; expo >>= 1; base = base * base; base %= mod; } return ans % mod; } ll C(ll n, ll m){ if(n < m)return 0; ll x = 1, y = 1; if(m > n - m)m = n - m; rep(i ,0 , m - 1){ x = x * (n - i) % mod; y = y * (i + 1) % mod; } return x * quickPow(y, mod - 2) % mod; } ll lucas(ll n, ll m){ return !m || n == m ? 1 : C(n % mod, m % mod) * lucas(n / mod, m / mod) % mod; } int prime[limit],tot,num[limit],phi[limit],miu[limit]; void get_prime(const int &n = 1e6){ memset(num,1,sizeof(num)); num[1] = num[0] = 0; miu[1] = 1; rep(i,2,n){ if(num[i])prime[++tot] = i,miu[i] = -1,phi[i] = i - 1; for(int j = 1; j <= tot && prime[j] * i <= n ; ++j){ num[prime[j] * i] = 0; if(i % prime[j] == 0){ miu[i * prime[j]] = 0; break; }else{ miu[i * prime[j]] = -miu[i];//莫比乌斯函数 } } } }//素数筛 int n, m,k; int a[limit]; int b[limit]; decltype(views::iota(1,2)) iter(int x, int y) { return views::iota(x, y + 1); } int all[30]; void solve(){ cin>>n; string str; cin>>str; memset(all, 0, sizeof(all)); str = ' ' + str; string ans; rep(i,1,n){ all[str[i] - 'a']++; } ans += str[1]; all[str[1] - 'a']--; int it = 2; while (it <= n){ if(str[it] < str[it - 1]){ int bj = it; while (it <= n and str[it] == str[bj]){ ans += str[it]; ++it; } }else{ break; } } string beta = ans; reverse(beta.begin(), beta.end()); ans += beta; cout<<ans<<endl; } int32_t main() { #ifdef LOCAL FOPEN; // FOUT; #endif FASTIO cin>>kase; // get_prime(3e5); while (kase--) solve(); cerr << "Time elapsed: " << 1.0*clock()/CLOCKS_PER_SEC << "s\n"; return 0; }
C题,给出一个序列,要求整个数组所有子串都满足 1/2 * (l + r) * (a[l] + a[r]) == ∑a[l...r],每次操作可以用将一个元素改成任意数值,问最小操作次数
首先利用高中知识我们得知只有等差数列满足我们的要求,那么怎么求等差数列呢?首先我们想到要计算出来所有的公差,答案必定是其中某一个公差,暴力尝试,发现喜提tle。仔细观察之后发现这个问题可以转化为求二维平面包含共线的点的最大的直线的数量,斜率即为公差,那么这个时候我们对于两点有d = (a[j] - a[i]) / (j - i),显然马上可以推出dp转移方程
dp[j][d] = max(dp[j][d],dp[i][d] + 1)
ans = max(dp[j][d],ans)
公差可以用map存放,也可以用gcd进行约分
然后答案为n - ans - 1
刚开始不太信任map对于double的精度支持,也没往约分这个方向想,后来在一个半小时的时候发现是我多虑了,map对于一般double完全没啥问题,。。。。哎,这可能是最大败笔了,白费一个小时在这个上面
复杂度(n^2 * logn),但立方好像也能过
#include <bits/stdc++.h> using namespace std; #define limit (500 + 5)//防止溢出 #define INF 0x3f3f3f3f #define inf 0x3f3f3f3f3f #define lowbit(i) i&(-i)//一步两步 #define EPS 1e-9 #define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0); #define ff(a) printf("%d\n",a ); #define pi(a,b) pair<a,b> #define rep(i, a, b) for(ll i = a; i <= b ; ++i) #define per(i, a, b) for(ll i = b ; i >= a ; --i) #define MOD 998244353 #define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next) #define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin) #define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout) typedef long long ll; typedef unsigned long long ull; char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; inline ll read(){ #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) ll sign = 1, x = 0;char s = getchar(); while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();} while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();} return x * sign; #undef getchar }//快读 void print(ll x) { if(x/ 10) print(x / 10); *O++=x % 10+'0'; } void write(ll x, char c = 't') { if(x < 0)putchar('-'),x = -x; print(x); if(!isalpha(c))*O++ = c; fwrite(obuf,O-obuf,1,stdout); O = obuf; } int kase; int n, m,k; int a[limit]; double b[limit]; int sum[limit]; void solve(){ cin>>n; rep(i,1,n){ cin>>a[i]; sum[i] = sum[i - 1] + a[i]; } map<double , int>mp[n + 5]; int ans = 0; rep(i,1,n){ rep(j,i + 1,n){ double diff = 1.0 * (a[j] - a[i]) / (j - i); mp[j][diff] = mp[i][diff] + 1; ans = max(ans, mp[j][diff]); } } ++ans; cout<<n-ans<<endl; } int32_t main() { #ifdef LOCAL FOPEN; // FOUT; #endif FASTIO cin>>kase; while (kase--) solve(); cerr << "Time elapsed: " << 1.0*clock()/CLOCKS_PER_SEC << "s\n"; return 0; }
D题套路结论题,不赘述了
E题很有意思
E题给出两个串s和t,每次只能交换相邻的两个元素,问使得s < t的最小交换次数,如果没有的话输出-1
首先看到交换相邻元素显然就是冒泡排序,冒泡排序显然就要求逆序对
这道题当时没什么思路,后来想想算是比较简单
首先字典序的判定方法是从左到右贪心选取的,所以我们在每个位置上放的策略也是固定的,空间其实就是s串小于t[i]的那些字符(假设存在),看看最靠前的几个下标挪动到当前位的费用,这个可以用树状数组求逆序对logn得出,如果不存在的话那就把这一位设置成和t[i]一样就好,如果不存在t[i]一样的那就直接break,维护一个tot值,记录前面都一样的费用。答案为 min(前面都一样的费用 + 当前位s[i] 小于 t[j]的最小费用)
#include <bits/stdc++.h> using namespace std; #define limit (1000000 + 5)//防止溢出 #define INF 0x3f3f3f3f #define inf 0x3f3f3f3f3f #define lowbit(i) i&(-i)//一步两步 #define EPS 1e-9 #define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0); #define ff(a) printf("%d\n",a ); #define pi(a,b) pair<a,b> #define rep(i, a, b) for(ll i = a; i <= b ; ++i) #define per(i, a, b) for(ll i = b ; i >= a ; --i) #define MOD 998244353 #define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next) #define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin) #define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout) typedef long long ll; typedef unsigned long long ull; char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; inline ll read(){ #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) ll sign = 1, x = 0;char s = getchar(); while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();} while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();} return x * sign; #undef getchar }//快读 void print(ll x) { if(x/ 10) print(x / 10); *O++=x % 10+'0'; } void write(ll x, char c = 't') { if(x < 0)putchar('-'),x = -x; print(x); if(!isalpha(c))*O++ = c; fwrite(obuf,O-obuf,1,stdout); O = obuf; } int kase; int n, m,k; int a[limit]; int tree[limit * 4]; void add(int x, int val){ for(int i = x; i <= n; i += lowbit(i)){ tree[i] += val; } } ll query(int x){ ll ans = 0; for(int i = x; i ; i -= lowbit(i)){ ans += tree[i]; } return ans; } string sorted(string s){ string x = s; sort(x.begin(), x.end()); return x; } void solve(){ cin>>n; string s,t; cin>>s>>t; string s1 = sorted(s); if(s1 >= t){ cout<<-1<<endl; return; } else if(s < t){ cout<<0<<endl; return; } s = ' ' + s; deque<int>q[30]; rep(i,1,n){ add(i, -query(i)); q[s[i] - 'a'].push_back(i); } ll ans = inf; ll tot = 0; rep(i,1,n){ int id = t[i - 1] - 'a'; ll shift = inf;; rep(j,0, id - 1){ if(q[j].size()){ shift = min(shift, q[j].front() - 1 - query(q[j].front())); } } if(shift != inf)ans = min(ans, tot + shift); if(q[id].empty())break; tot += q[id].front() - 1 - query(q[id].front()); add(q[id].front(), 1); q[id].pop_front(); } cout<<(ans < inf ? ans : -1)<<endl; } int32_t main() { #ifdef LOCAL FOPEN; // FOUT; #endif FASTIO cin>>kase; while (kase--) solve(); cerr << "Time elapsed: " << 1.0*clock()/CLOCKS_PER_SEC << "s\n"; return 0; }