(注:本來開了個坑,原標題為「do you know wtf it is???」,正文為「懵逼了吧」,所以被 hsc 罵了……)
這場比較簡單,所以本文的重心放在一波三折的比賽(賽后?)經歷上(
本來想着用 tzcWh... 這個號控制住不要上橙,大概控制在 2070~2099 左右,然后下一場 div. 2 就能超過大號這樣。
比賽結束前 5min E 交上去 WA 了,於是棄了,看了眼 predictor,能直接上 2150,這不行啊。於是開始故意 hack 失敗,網速比較慢所以 5min 只 hack 了 5 次。
減了 250pts 之后比賽結束,又看了一眼 predictor,+130?掐指一算,漲到 2102?wtf???要上橙就給我上高一點,要么就不要上橙,ntm 給我搭個橙名線是幾個意思?很后悔沒有早點開始 hack,多 hack 一次就 CM 穩了。然后就跟 wjz 瘋狂祖安。
然后突然發現我的 A 好像 FST 了,上面出現了個紅色的 -1?那 tgxl,rating 低一點沒關系,至少不用上橙了!然后 wjz 說「這是 feature,你刷新一下」(老 system test 觀眾了),然后又沒 FST,得分了。還我 FST!!!於是繼續祖安,祖安累了去洗了個澡。
洗完澡回來之后發現 wjz 跟我說「不會 master」,又是啥?system test 已經結束了,打開 predictor 一看 +124,2096?tgxl,這個結果我滿意。於是開開心心睡覺去了。
早上起來忐忑地打開電腦,發現居然 +127,2099?這無疑是我最滿意的結果,請叫我控分帶師(
也許這就是命運吧(
下面是題解(正文部分)
CF 比賽頁面傳送門
A - Subset Mex
洛谷題目頁面傳送門 & CF 題目頁面傳送門
給定一個集合 \(a,|a|=n\),將它分成兩個集合 \(A,B\),要求最大化 \(\operatorname{mex}(A)+\operatorname{mex}(B)\)。本題多測。
\(n\in[1,100],a_i\in[0,100],T\in[1,100]\)。
隨便貪心就好了,兩個集合的 \(\operatorname{mex}\) 齊頭並進,某個進不了了就停下來,兩個都停下來的時候就是答案最大化的情況。代碼也隨便寫了吧。
#include<bits/stdc++.h>
using namespace std;
const int N=100;
int n;
int a[N+1];
int cnt[N+1];
void mian(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)cnt[a[i]]++;
int fst=0;
for(int i=0;i<=100;i++)if(cnt[i]<2){fst=i;break;}
for(int i=fst;i<=100;i++)if(cnt[i]<1)return cout<<fst+i<<"\n",void();
if(fst)cout<<fst+101<<"\n";
else cout<<202<<"\n";
}
int main(){
int testnum=1;
cin>>testnum;
while(testnum--)mian();
return 0;
}
B - Maximum Product
洛谷題目頁面傳送門 & CF 題目頁面傳送門
給定 \(n\) 個整數(可正可負可零),求其中 \(5\) 個數的乘積的最大值。本題多測。
\(n\in\left[5,10^5\right],\sum n\leq 2\times10^5\)。
這個就分個兩種情況吧,有 \(0\) 和無 \(0\)。
有 \(0\) 的話就是 \(0\) 了。
無 \(0\) 的話就枚舉正數個數,然后排個序貪個心就切了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
const int inf=0x3f3f3f3f3f3f3f3f;
int n;
void mian(){
cin>>n;
vector<int> po,ne;
int ans=-inf;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(x>0)po.pb(x);
if(x==0)ans=0;
if(x<0)ne.pb(x);
}
sort(po.begin(),po.end(),greater<int>());
sort(ne.begin(),ne.end());
for(int i=0;i<=5;i++){
if(i<=po.size()&&5-i<=ne.size()){
int res=1;
if(5-i&1){
for(int j=(int)(po.size())-1;j>=(int)(po.size())-i;j--)res*=po[j];
for(int k=(int)(ne.size())-1;k>=(int)(ne.size())-(5-i);k--)res*=ne[k];
}
else{
for(int j=0;j<i;j++)res*=po[j];
for(int k=0;k<5-i;k++)res*=ne[k];
}
ans=max(ans,res);
}
}
cout<<ans<<"\n";
}
signed main(){
int testnum=1;
cin>>testnum;
while(testnum--)mian();
return 0;
}
C - Link Cut Centroids
洛谷題目頁面傳送門 & CF 題目頁面傳送門
給定一棵大小為 \(n\) 的樹。要求刪掉一條邊加上一條邊使重心唯一。給出方案。本題多測。
\(n\in\left[3,10^5\right],\sum n\leq 10^5\)。
假如說本來就唯一的話不用說。否則:
顯然兩個重心相鄰。欽定其中一個為根,那么另一個就是第 \(2\) 層。我也不知道怎么想出來的,就隨便試吧,可以刪掉第 \(2\) 層重心的任意一條通向兒子的邊(由 \(n\geq 3\) 易證一定有兒子),然后把那個兒子連向根即可。
證明的話,顯然刪加過之后根依然是重心,因為子樹大小最大值變小了嘛。而第 \(2\) 層那個的相對應的顯然變大了,那么它們的最大子樹大小就不等了,就不可能同時為重心。然后如何證其他點不為重心呢?因為要想是重心就必須與根相鄰,而這三個點發生關系跟其他兒子有個屁的關系,那最大子樹大小肯定就不變啊,就無法翻身。得證。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=100000;
int n;
vector<int> nei[N+1];
int sz[N+1];
void dfs(int x=1,int fa=0){
sz[x]=1;
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i];
if(y==fa)continue;
dfs(y,x);
sz[x]+=sz[y];
}
}
void mian(){
cin>>n;
for(int i=1;i<=n;i++)nei[i].clear();
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
nei[x].pb(y);nei[y].pb(x);
}
dfs();
vector<int> cen;
for(int i=1;i<=n;i++){
bool flg=true;
int sum=1;
for(int j=0;j<nei[i].size();j++){
int x=nei[i][j];
if(sz[x]>sz[i])continue;
flg&=sz[x]<=n/2;
sum+=sz[x];
}
flg&=n-sum<=n/2;
if(flg)cen.pb(i);
}
assert(cen.size()<=2);
if(cen.size()==1)printf("%d %d\n%d %d\n",1,nei[1][0],1,nei[1][0]);
else{
int son=nei[cen[0]][0]==cen[1]?nei[cen[0]][1]:nei[cen[0]][0];
printf("%d %d\n%d %d\n",cen[0],son,cen[1],son);
}
}
int main(){
int testnum=1;
cin>>testnum;
while(testnum--)mian();
return 0;
}
D - Three Sequences
洛谷題目頁面傳送門 & CF 題目頁面傳送門
給定一個長度為 \(n\) 的數列 \(a\)。你需要構造出長度為 \(n\) 的數列 \(b,c\),滿足 \(a_i=b_i+c_i\),且 \(b\) 不降,\(c\) 不升。最小化 \(\max(b_i,c_i)\)。然后還有 \(q\) 次區間增加,每次輸出最小化的結果。
\(n,q\in\left[1,10^5\right]\)。
二話不說先找結論啊。通過觀察樣例發現,一開始 \(b_1,c_1\) 隨便取只要滿足 \(a_1=b_1+c_1\) 即可,然后以后的話,若 \(a\) 的增量 \(\geq 0\) 就在 \(b\) 上加,否則就在 \(c\) 上減。證明的話隨便想想很簡單,顯然 \(\max(b_i,c_i)=\max(b_n,c_1)\),那么在 \(b_1,c_1\) 固定的時候,\(b\) 的總增量顯然越小越好,於是就只有在必要的時候才在 \(b\) 上加咯。
然后現在解決 \(b_1,c_1\) 不固定的事情。我們想要令 \(\max(b_n,c_1)\) 這個柿子最小,那么首先需要將 \(b_n\) 用 \(b_1\) 表示一下。令增量 \(\Delta_i=a_{i+1}-a_i\),則 \(b_n=b_1+\sum\limits_{i=1}^{n-1}[\Delta_i\geq 0]\Delta_i\)。令那個 \(\sum\) 為 \(\Sigma\),那么柿子為 \(\max(b_1+\Sigma,c_1)\)。又 \(a_1=b_1+c_1\),則柿子又可以寫為 \(\max(b_1+\Sigma,a_1-b_1)\)。那么注意到 \(\max\) 兩個參數是和為常量的,那么最理想的情況是令它們相等,則 \(\max\) 最小。解個方程可以得到 \(b_1=\dfrac{a_1-\Sigma}2\)。但是不允許出現小數,所以還要取個整然后左右兩邊 round 一下。
然后還有區間加呢。注意到區間加只會令兩個增量變化,於是隨便 \(\mathrm O(1)\) 維護即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x3f3f3f3f3f3f3f3f;
const int N=100000;
int n;
int a[N+1];
int qu;
int d[N+1];
int calc(int now,int x){
int res=inf;
for(int i=now-3;i<=now+3;i++)res=min(res,max(i+x,a[1]-i));
return res;
}
void mian(){
cin>>n;
for(int i=1;i<=n;i++)scanf("%lld",a+i);
for(int i=1;i<n;i++)d[i]=a[i+1]-a[i];
int x=0;
for(int i=1;i<n;i++)if(d[i]>0)x+=d[i];
cout<<calc(a[1]-x>>1,x)<<"\n";
cin>>qu;
while(qu--){
int l,r,v;
scanf("%lld%lld%lld",&l,&r,&v);
if(l==1)a[1]+=v;
if(l-1&&d[l-1]>0)x-=d[l-1];
if(r<n&&d[r]>0)x-=d[r];
d[l-1]+=v,d[r]-=v;
if(l-1&&d[l-1]>0)x+=d[l-1];
if(r<n&&d[r]>0)x+=d[r];
printf("%lld\n",calc(a[1]-x>>1,x));
}
}
signed main(){
int testnum=1;
// cin>>testnum;
while(testnum--)mian();
return 0;
}
E - Deleting Numbers
這是釣魚王子出的好題哦~
洛谷題目頁面傳送門 & CF 題目頁面傳送門
本題為交互題。給定 \(n\),初始時有 \(A=[1,n]\cap \mathbb Z\)。你有 \(3\) 種操作:
- 查詢 \(A\) 中有多少個 \(a\) 的倍數(\(a\in [1,n]\));
- 查詢 \(A\) 中有多少個 \(a\) 的倍數(\(a\in[2,n]\)),並將 \(A\) 中所有 \(a\) 的倍數刪去,特殊地,如果 \(x\) 是 \(a\) 的倍數則不刪;
- 得出答案 \(a\),操作之后立即結束。
你需要在不超過 \(10^4\) 次操作內猜出某個預先設定好的 \(x\in A\)。
\(n\in\left[2,10^5\right]\)。
思路在於,通過操作 \(1\) 和操作 \(2\) 的配合,實現查詢 \(x\) 是否是 \(a\) 的倍數。
不難想到嘗試將 \(x\) 分解質因數,根據弱智的唯一分解定理可以確定 \(x\)。
最 naive 的想法是每個質數的冪都試一遍。打個表發現 \(\pi(n)\) 大概是 \(9500+\),然后質數的冪大概是 \(9700\) 左右個。你可能會說,噫,好,這場 div. 2 我阿克了!哦上帝,瞧瞧這天真的聲音。注意到操作 \(2\) 是先返回結果再刪除的,也就是說它的返回結果是個幌子,你想知道 \([a\mid x]\) 必須要先 \(2\) 再 \(1\),\(2\) 步走。
想到壽司晚宴那題的一個結論:將質數分成小質數和大質數,則每個數最多含有一個大質因子。
那么先將小質因子隨便毛搞搞,我們設 \(\tau(m)\) 表示 \(m\) 以內質數的冪的個數,那么是 \(2\tau(\sqrt n)\) 步的。實際上可以進一步優化,對於每個質數,先將它給 \(2\) 了,然后每個冪就可以直接查了。這樣是 \(\pi(\sqrt n)+\tau(\sqrt n)\) 的。別看這一步優化微不足道,其實她能決定你是否 AC。
現在把質因數分解式里的小質因子部分已經分解出來了,並且 \(A\) 里顯然只剩下 \(1\) 和 \(x\) 和所有大質數。接下來的任務就是要找出 \(x\) 是否有大質因子,如果有的話是誰。
需要分出兩種情況:
- 小質因子部分 \(>1\)。那么顯然 \(x\) 是不屬於那個大質數集的。那還怕個鬼啊,直接檢查所有的大質數乘以小質因子部分是否剩一個數,剩的話大質因子就是他了乘上去,否則沒有;
- 小質因子部分 \(=1\)。此時就需要害怕了,因為你「檢查所有的大質數乘以小質因子部分是否剩一個數」的話,那你任何一次檢查結果都是「是」,就無語了。而此時問題變得更加簡單,剩下來的集合就是 \(1\) 和所有大質數,而你可以確定 \(x\) 就在里面。考慮對大質數序列分塊。每塊整體刪一下,然后如果集合大小減少數量不對勁就說明 \(x\) 一定在這塊里面,集中精力搞。由於塊大小不大,可以直接逐個用操作 \(1\) 排查。總操作次數大約為 \(\pi(\sqrt n)+\tau(\sqrt n)+(\pi(n)-\pi(\sqrt n))+2\sqrt{\pi(n)-\pi(\sqrt n)}=\tau(\sqrt n)+\pi(n)+2\sqrt{\pi(n)-\pi(\sqrt n)}\),卡的死死的,出題人真是毒瘤。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
bool ispr(int x){
if(x<2)return false;
for(int i=2;i*i<=x;i++)if(x%i==0)return false;
return true;
}
int A(int x){
printf("A %d\n",x),fflush(stdout);
cin>>x;return x;
}
int B(int x){
printf("B %d\n",x);fflush(stdout);
cin>>x;return x;
}
void C(int x){printf("C %d\n",x);fflush(stdout);exit(0);}
void mian(){
int n;
cin>>n;
if(n==1)C(1);
vector<int> pr;
for(int i=2;i<=n;i++)if(ispr(i))pr.pb(i);
int x=1,lim=sqrt(n);
for(int i=0;pr[i]<=lim;i++){
B(pr[i]);
int now=1;
while(now*pr[i]<=n)now*=pr[i];
while(now>1){
if(A(now)==1){x*=now;break;}
now/=pr[i];
}
}
vector<int> v;
for(int i=0;i<pr.size();i++)if(pr[i]>lim)v.pb(pr[i]);
if(x==1){
int now=A(1);
for(int i=0;i<v.size();i+=100){
for(int j=i;j<i+100&&j<v.size();j++)B(v[j]);
int res=A(1);
if(now-res==min(i+100,int(v.size()))-i)now=res;
else{
for(int j=i;j<i+100&&j<v.size();j++)if(A(v[j])==1){x=v[j];break;}
break;
}
}
}
else{
for(int i=0;i<v.size();i++)if(1ll*x*v[i]<=n&&A(x*v[i])==1){x*=v[i];break;}
}
C(x);
}
int main(){
int testnum=1;
// cin>>testnum;
while(testnum--)mian();
return 0;
}