做得很糟糕。。。
A
只有\(n=2\)且两者之间的距离大于1时才可以插入。
#include<bits/stdc++.h> using namespace std; using ll=long long; const int MAXN=2e5+10; const ll mod=998244353; void solve() { int n; cin>>n; std::vector<int> v(n,0); for(auto &t: v) cin>>t; if(n>2) { cout<<"NO\n"; return; } if(v[1]-v[0]<2) { cout<<"No\n"; return; } cout<<"YES\n"; } int main() { int t=1; cin>>t; while(t--) solve(); return 0; }
|
B
直接找最小的区间即可。需要考虑左右端点不重合的情况,另外加一。
此外还需要特判不重合的情况,直接为1即可。
#include<bits/stdc++.h> using namespace std; using ll=long long; const int MAXN=2e5+10; const ll mod=998244353; void solve() { int l,r; cin>>l>>r; int L,R; cin>>L>>R; if(r<L || R<l) { cout<<1<<"\n"; return; } int ans=min(R,r)-max(L,l); if(R!=r) ans++; if(l!=L) ans++; cout<<ans<<"\n"; } int main() { int t=1; cin>>t; while(t--) solve(); return 0; }
|
C
博弈论,显然需要先排个序,然后依次取。
需要考虑的是,偶数位的数字是Bob可以修改的,但是要考虑前提是修改后不能大于前一位数字(也就是Alice取的),否则顺序会颠倒。
用差分计算每一个偶数位最多可以加的数,根据\(k\)辅助判断即可。
#include<bits/stdc++.h> using namespace std; using ll=long long; const int MAXN=2e5+10; const ll mod=998244353; void solve() { ll n,k; cin>>n>>k; std::vector<ll> v(n,0); for(int i=0;i<n;i++) cin>>v[i]; sort(v.begin(),v.end(),[&](ll x,ll y){return x>y;}); ll a=0,b=0; std::vector<ll> need; for(int i=0;i<n;i++) { if(i%2==0) a+=v[i]; else b+=v[i]; if(i%2) { need.push_back(v[i-1]-v[i]); } } ll d=a-b; for(auto &t: need) { if(k>=t && d-t>=0) k-=t,d-=t; else if(d-t>=0 && k<t) { d-=k; break; } } cout<<d<<"\n";
} int main() { int t=1; cin>>t; while(t--) solve(); return 0; }
|
D
可以学到一些\(trick\)的好题,这里可以使用位运算进行处理颜色。
我们要的是距离最小,可以维护每一个城市的左右边,拥有不同颜色且可以直接到达的城市(也就是一个颜色相同、一个颜色不同)。通过中间点,不断的跳跃,可以计算得到需要的距离。
需要预处理的就是\(left\)数组和\(right\)数组,可以使用类似\(dp\)的过程进行处理。
以\(right\)数组为例,如果\(city_i\)与\(city_{i+1}\)有完全相同的颜色或者没有相同的颜色,
那么\(right_i=right_{i+1}\),因为如果相同,那么直接继承即可。否则,根据\(right\)数组的定义,肯定有一个颜色是\(city_i\)和\(city_{right[i+1]}\)都有的,\(left\)数组也是类似的推导。
需要注意的是:跳跃是通过中间点的,一些细节还是看代码。
#include<bits/stdc++.h> using namespace std; using ll=long long; const int MAXN=2e5+10; const ll mod=998244353; void solve() { int n,q; cin>>n>>q; std::vector<int> city(n); std::string s; for(int i=0;i<n;i++) { cin>>s; for(auto t: s) { if(t=='B') city[i]|=(1<<0); else if(t=='G') city[i]|=(1<<1); else if(t=='R') city[i]|=(1<<2); else city[i]|=(1<<3); } } std::vector<int> left(n,0); std::vector<int> right(n,0); right[n-1]=n; left[0]=-1; for(int i=n-2;i>=0;i--) { if(city[i]==city[i+1] || (city[i] & city[i+1])==0) { right[i]=right[i+1]; } else right[i]=i+1; } for(int i=1;i<n;i++) { if(city[i]==city[i-1] || (city[i] & city[i-1])==0) { left[i]=left[i-1]; } else left[i]=i-1; } while(q--) { int x,y; cin>>x>>y; x-=1,y-=1; if(x==y) cout<<"0\n"; else { if(x>y) swap(x,y); int ans1=0; int tmp=x; while(x<n && (city[x] & city[y])==0) { ans1+=right[x]-x; x=right[x]; } if(x<n) ans1+=abs(x-y); else ans1=(1<<30); x=tmp;
int ans2=0; while(y>=0 && (city[x] & city[y])==0) { ans2+=y-left[y]; y=left[y]; } if(y>=0) ans2+=abs(x-y); else ans2=(1<<30); int ans=min(ans1,ans2); if(ans>=(1<<30)) cout<<-1<<"\n"; else cout<<ans<<"\n"; } } } int main() { int t=1; cin>>t; while(t--) solve(); return 0; }
|
E
博弈论,本来自己觉得补不了,在大佬的督促下去补了。正好可以复习下\(SG\)函数,才发现自己以前对这个完全没有正确的认识,正好找这个机会补一补。(发现理解了\(SG\)函数也没有那么难)。
有时间的话看这篇文章:讲明白了\(SG\)函数到底是怎么来的:浅谈SG函数和博弈论 -
洛谷专栏 (luogu.com.cn)
先弄一些前置知识:策梅洛定理。
我们考虑对于一个游戏,他满足以下的特点
- 两人单挑,轮流操作
- 信息公开透明
- 没有随机因素
- 有限步内必然结束
- 不存在平局
策梅洛定理:对于这样的一个游戏,任何一个局面先手或者后手其中之一必然存在必胜策略
证明:
- 如果已经到了最终状态,按照游戏规则,必然有一方已经获胜
- 如果当前状态所有后继都导向先手必胜,那么本状态先手必败
- 如果当前状态可以导向先手必败,那么本状态先手必胜
所以每一个状态的胜负都可以被确定。
所以这些游戏可以使用记忆化搜索暴力解决。
回到题目:这种题还是得打表比较好(不会打表。。。)
简单总结就是,偶数的\(SG\)函数值都是0,质数的\(SG\)值就是他在质数表中的位置,合数的\(SG\)函数值就是它的最小质因子的\(SG\)函数值。
#include<bits/stdc++.h> using namespace std; using ll=long long; const ll mod=1e9+7; const int MAXN=1e7+10; std::vector<int> sg(MAXN,0); void work(int n) { std::vector<bool> isp(n,1); std::vector<ll> prime; std::vector<int> tmp(n,0); isp[0]=isp[1]=0; for(ll i=2;i<=n;i++) { if(isp[i]) { prime.push_back(i); tmp[i]=prime.size(); } for(auto t: prime) { if(i*t>n) break; isp[i*t]=0; if(i%t==0) break; } }
sg[0]=0; sg[1]=1; for(int i=2;i<=n;i++) { if(i%2==0) sg[i]=0; else if(isp[i]) sg[i]=tmp[i]; else { for(auto t: prime) { if(i%t==0) { sg[i]=sg[t]; break; } } } } } void solve() { int n; cin>>n; int ans=0,x=0; for(int i=0;i<n;i++) { cin>>x; ans^=sg[x]; } if(ans) cout<<"Alice\n"; else cout<<"Bob\n"; } int main() { work(MAXN); int t=1; cin>>t; while(t--) solve(); return 0; }
|
下面是打表的程序:
#include<bits/stdc++.h> using namespace std; using ll=long long; const ll mod=1e9+7; const int MAXN=1e7+10; int mex(auto v ,int x) { unordered_set<int> S; for(int i=0;i<v.size();i++) { if(__gcd(i,x)==1 && i!=0) S.insert(v[i]); } for (int i = 0;; ++i) { if(S.find(i) == S.end()) return i; } } void work() { std::vector<int> sg(3,0); sg[0]=0; sg[1]=1; sg[2]=0; for(int i=3;i<=100;i++) { int val=mex(sg,i); sg.push_back(val); cout<<i<<": "<<val<<"\n"; } } void solve() {
} int main() { work(); int t=1; cin>>t; while(t--) solve(); return 0; }
|
对应的输出结果:
3: 2 4: 0 5: 3 6: 0 7: 4 8: 0 9: 2 10: 0 11: 5 12: 0 13: 6 14: 0 15: 2 16: 0 17: 7 18: 0 19: 8 20: 0 21: 2 22: 0 23: 9 24: 0 25: 3 26: 0 27: 2 28: 0 29: 10 30: 0 31: 11 32: 0 33: 2 34: 0 35: 3 36: 0 37: 12 38: 0 39: 2 40: 0 41: 13 42: 0 43: 14 44: 0 45: 2 46: 0 47: 15 48: 0 49: 4 50: 0 51: 2 52: 0 53: 16 54: 0 55: 3 56: 0 57: 2 58: 0 59: 17 60: 0 61: 18 62: 0 63: 2 64: 0 65: 3 66: 0 67: 19 68: 0 69: 2 70: 0 71: 20 72: 0 73: 21 74: 0 75: 2 76: 0 77: 4 78: 0 79: 22 80: 0 81: 2 82: 0 83: 23 84: 0 85: 3 86: 0 87: 2 88: 0 89: 24 90: 0 91: 4 92: 0 93: 2 94: 0 95: 3 96: 0 97: 25 98: 0 99: 2 100: 0
|