C-Assembly via Remainders
思路:因為xi最大隻有500,而構造的ai最大可以到1e9,直接從501開始構造即可。
void solve(){ //C 簡單構造
int n; cin>>n;
vector<int> vct;
vct.emplace_back(501);
for(int i=2;i<=n;i++){
int x; cin>>x;
vct.emplace_back(vct[i-2]+x);
}
for(int i=0;i<n;i++) cout<<vct[i]<<" ";
cout<<endl;
}
D-Permutation Game
思路:按著順序列舉即可。列舉每一個位置,可以計算出此後一直拿這個位置可以得到的最終的值。取max即可.計算雙方的最大可能得分來回答這個問題。
int p[200005];
int arr[200005];
void solve(){ //D 列舉n,貪心
int n,k,sa,sb; cin>>n>>k>>sa>>sb;
for(int i=1;i<=n;i++) cin>>p[i];
for(int i=1;i<=n;i++) cin>>arr[i];
// vector<int> pa,pb;
// pa.emplace_back(sa);
// pb.emplace_back(sb);
// for(int i=2;i<=n;i++) pa.emplace_back(p[pa[i-2]]);
// for(int i=2;i<=n;i++) pb.emplace_back(p[pb[i-2]]);
int maxa=INT_MIN,suma=0,maxb=INT_MIN,sumb=0;
for(int i=1;i<=k&&i<=n;i++){
suma+=arr[sa];
maxa=max(maxa,suma+(k-i)*arr[sa]);
sa=p[sa];
sumb+=arr[sb];
maxb=max(maxb,sumb+(k-i)*arr[sb]);
sb=p[sb];
}
if(maxa>maxb) cout<<"Bodya"<<endl;
else if(maxb>maxa) cout<<"Sasha"<<endl;
else cout<<"Draw"<<endl;
}
E-Cells Arrangement
思路:集合最大的值為(n-1+n-1).選了(1,1)和(1,2)可以貢獻0,1;兩個值.再選(n,n)可以貢獻(n-1+n-1)和(n-1+n-1-1). 再選(n-1,n-1)可以貢獻(n-1-1+n-1-1)和(n-1-1+n-1-1-1)都是會貢獻兩個不重複的,沒出現過的值。
舉例:n=4;
選擇(1,1)和(1,2)-->S={0,1};
選擇(4,4)-->S={0,1,6,5};
再選擇(3,3)-->S={0,1,6,5,4,3}; 呃呃這個應該是S={0,1,6,5,4,3,2}這樣選就產生了所有可能的距離。。
可是為什麼不會存在一個點,選了這個點可以貢獻3個沒出現過的值的呢?原因如上...呃呃歪打誤撞寫對了正解。。
主對角線到(1,1)產生偶數距離,到(1,2)產生奇數距離.
void solve(){ //E 巧妙構造題,如果想歪了的話,將會一直卡死 30分鐘
int n; cin>>n;
cout<<"1 1"<<endl;
cout<<"1 2"<<endl;
for(int i=n;i>=3;i--) cout<<i<<" "<<i<<endl;
cout<<endl;
}
F-Equal XOR Segments
思路:不錯的題!
第一步:首先需要 發現 最終區分的區間都可以是兩段或三段。
因為大於三段的區間,都可以透過x^x^x=x化簡為兩段或三段。
發現上面性質之後,就可以進行下一步了。
第二步:
////F-Equal XOR Segments-相等的 XOR 段--好題
////手寫二分
int n,q;
int pre[200005]; ////經典的異或字首和
void solve(){ ////F o(qlogn) 異或+二分(不好想到)
cin>>n>>q;
////關鍵就在於此。mp是用作二分的容器。 first為字首異或的值--second為這些相同的字首異或值出現的下標..(需要對這些下標進行二分)
map<int,vector<int>> mp; ////TLE30..cf不要再用unordered_map了。。會專門被卡。。
for(int i=1;i<=n;i++){
cin>>pre[i];
pre[i]^=pre[i-1];
mp[pre[i]].emplace_back(i); ////vector中存的是下標
}
while(q--){
int l,r; cin>>l>>r;
bool ans=false;
////先檢查能不能分為兩段
if(pre[l-1]==pre[r]) ans=true;
////再檢查能不能分為三段
if(!ans){
int num1=pre[l-1],num2=pre[r];
int minidxa=INT_MAX,maxidxb=INT_MIN;
// vector va=mp[num1],vb=mp[num2]; //這個操作是時間複雜度是o(n)的,空間複雜度也是o(n)的
int ll=0,rr=(int)mp[num2].size()-1; ////注意這裡的ll和rr是 二分的是下標 --- 二分下標找在區間l,r中的下標..
while(ll<=rr){
int mid=(ll+rr)>>1;
if(l<=mp[num2][mid]&&mp[num2][mid]<r){
minidxa=mp[num2][mid];
rr=mid-1;
}
else if(mp[num2][mid]>=r) rr=mid-1;
else if(mp[num2][mid]<=l) ll=mid+1;
}
ll=0,rr=(int)mp[num1].size()-1; ////超乎想象。。mp.size()不加強制型別轉換,在cf上跑會導致RE,找了很久都找不到錯。。原來這都會導致RE。
while(ll<=rr){
int mid=(ll+rr)>>1;
if(l<mp[num1][mid]&&mp[num1][mid]<r){
////右邊可以不取等號,左邊需要取等號。按定義的話,左邊取等就是第一個單獨一個區間,右邊取等沒意義,越界了。第二個斷點取r-1的時候,r就是單獨一個區間的含義了。
maxidxb=mp[num1][mid];
ll=mid+1;
}
else if(mp[num1][mid]>=r) rr=mid-1;
else if(mp[num1][mid]<=l) ll=mid+1;
}
if( l<=minidxa && minidxa<maxidxb && maxidxb<r ) ans=true;
}
if(ans) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
cout<<endl;
}