NOIP2020 退役記
把原來寫的東西全刪了。畢竟留下了不少遺憾,也讓我自己失望了吧。
12:30 想到 T3 怎么做,寫完后發現第二個樣例過不去,也就爆零了。
晚上回家理順了一遍思路,寫了半個小時不到就寫完並在 oitiku 上 AC 了。我刷了不少 AGC 和 CF 的構造題,原本只是准備省選和 NOI 寫的,結果 NOIP 考到了。我以為我能暴切 T3 考到省隊線,結果被 T3 送退役了。
估分 \(80\sim 100+84\sim 100+0+60\sim 80=224\sim 280\)。不過應該省選還是會去的,但是翻盤的可能性不大了。
不過我也努力過了,這次的失誤也是我想題較慢的緣故。若是把這道 T3 放到省選或 NOI 上,我感覺自己能現場寫出來的。太遺憾了,實在是太遺憾了。
要是 T3 一點都不會做該多好,這樣我就可以安心退役了。我真的心有不甘啊啊啊啊啊啊。
唉,在絕望中給人希望是一件多么殘酷的事情。
T3 做法:
考慮分治,將顏色編號 \(\le mid\) 和 \(>mid\) 分為兩個集合。這樣問題轉化為只用若干個長度為 \(m\) 的 \(01\) 串,將要求每個串全部 \(0/1\)。
接着考慮兩個串 \(S,T\)。可以知道,\(cnt_{S,0/1}+cnt_{T,0/1}\) 總有一個 \(\ge m\)。那么,我們可以通過如下構造,得到一個串全部 \(0/1\)。
設空串為 \(now\)。取 \(S,T\) 的后 \(\lfloor \frac m2\rfloor\) 位,放到 \(now\) 中。如果是 \(0\) 放到 \(S\),\(1\) 放到 \(T\),那么可以得到一個長為 \(\lfloor \frac m2\rfloor\) 的串全部 \(0/1\)。若是 \(S\),再將 \(S\) 全部放到 \(now\)。此時 \(S\) 為空串,\(T\) 沒有性質,\(now\) 前 \(\lfloor \frac m2\rfloor\) 位相等。此處用了 \(3m\) 次操作。
對於串 \(now\) 和 \(T\),我們取 \(now\) 的后 \(\lceil \frac m2\rceil\) 位,\(T\) 的后 \(\lfloor \frac m2\rfloor\) 位,執行類似剛才的操作。此時 \(now\) 的后 \(\lceil \frac m2\rceil\) 位相等。此處用了 \(2m\) 次操作。
現在討論 \(now\) 前 \(\lfloor \frac m2\rfloor\) 位和后 \(\lceil \frac m2\rceil\) 位是否相等。若相等,那么我們已經構造了出來。否則,將 \(now\) 的后 \(\lceil \frac m2\rceil\) 位放到 \(S\),然后對 \(T\) 執行剛才的過程。如果是 \(0\) 放到 \(S\),\(1\) 放到 \(now\)。這樣 \(S/now\) 一定有一個串符合條件,用了 \(\frac 32m\) 次操作。
上界大概是 \(O(F(n)\times \frac {13}{2}m)\),\(F(n)=\sum (len_{rt}-1)\),\(rt\in\) 長度為 \(n\) 的線段樹。\(F(50)=237\),可以放心寫,不怎么卡常數。
#include <bits/stdc++.h>
using namespace std;
const int maxn=55;
const int maxa=820005;
int n,m,x_[maxa],y_[maxa],sz,now;
vector<int> g[maxn];
inline void add(int x,int y)
{
x_[++sz]=x,y_[sz]=y;
g[y].push_back(g[x].back());
g[x].pop_back();
}
void solve(int l,int r,vector<int> &id)
{
if(id.size()==1) return;
int mid=(l+r)>>1;
vector<int> A,B; A.clear(),B.clear();
while(!id.empty())
{
int s=id.back(); id.pop_back();
int t=id.back(); id.pop_back();
// s, t -> now
for(int i=0;i<m/2;i++) add(s,now),add(t,now);
int pa=0,pb=0;
for(int i=0;i<g[now].size();i++)
if(g[now][i]<=mid) pa++;
else pb++;
int op1=(pa>=m/2)?0:1;
while(!g[now].empty())
{
if(((op1==0 && g[now].back()<=mid) || (op1==1 && g[now].back()>mid)) && g[s].size()<m) add(now,s);
else add(now,t);
}
for(int i=0;i<m;i++) add(s,now);
for(int i=0;i<m/2;i++) add(t,s);
for(int i=0;i<(m+1)/2;i++) add(now,s);
int qa=0,qb=0;
for(int i=0;i<g[s].size();i++)
if(g[s][i]<=mid) qa++;
else qb++;
int op2=(qa>=(m+1)/2)?0:1;
while(!g[s].empty())
{
if(((op2==0 && g[s].back()<=mid) || (op2==1 && g[s].back()>mid)) && g[now].size()<m) add(s,now);
else add(s,t);
}
if((g[now][0]<=mid)==(g[now].back()<=mid))
{
if(g[now].back()<=mid) A.push_back(now);
else B.push_back(now);
if(id.empty())
{
if(g[t].back()<=mid) A.push_back(t);
else B.push_back(t);
}
else id.push_back(t);
now=s;
}
else
{
// op1 -> now, op2 -> s
for(int i=0;i<(m+1)/2;i++) add(now,s);
int x=0,y=0;
for(int i=0;i<g[t].size();i++)
if(g[t][i]<=mid) x++;
else y++;
if(op2==0)
{
if(x+g[s].size()>=m)
{
while(!g[t].empty())
{
if(g[t].back()<=mid && g[s].size()<m) add(t,s);
else add(t,now);
}
A.push_back(s);
if(id.empty())
{
if(g[now].back()<=mid) A.push_back(now);
else B.push_back(now);
}
else id.push_back(now);
now=t;
}
else
{
while(!g[t].empty())
{
if(g[t].back()>mid && g[now].size()<m) add(t,now);
else add(t,s);
}
B.push_back(now);
if(id.empty())
{
if(g[s].back()<=mid) A.push_back(s);
else B.push_back(s);
}
else id.push_back(s);
now=t;
}
}
else
{
if(y+g[s].size()>=m)
{
while(!g[t].empty())
{
if(g[t].back()>mid && g[s].size()<m) add(t,s);
else add(t,now);
}
B.push_back(s);
if(id.empty())
{
if(g[now].back()<=mid) A.push_back(now);
else B.push_back(now);
}
else id.push_back(now);
now=t;
}
else
{
while(!g[t].empty())
{
if(g[t].back()<=mid && g[now].size()<m) add(t,now);
else add(t,s);
}
A.push_back(now);
if(id.empty())
{
if(g[s].back()<=mid) A.push_back(s);
else B.push_back(s);
}
else id.push_back(s);
now=t;
}
}
}
}
solve(l,mid,A),solve(mid+1,r,B);
}
int main()
{
freopen("ball.in","r",stdin);
freopen("ball.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int x; scanf("%d",&x);
g[i].push_back(x);
}
vector<int> id; id.clear();
for(int i=1;i<=n;i++) id.push_back(i);
now=n+1,solve(1,n,id);
printf("%d\n",sz);
for(int i=1;i<=sz;i++)
printf("%d %d\n",x_[i],y_[i]);
return 0;
}