P7115 [NOIP2020] 移球游戏 解法
题意
有 \(n+1\) 根柱子,每个柱子能放下 \(m\) 个球,其中前 \(n\) 个上放满了颜色不同的球,共 \(n\) 种颜色,每种颜色 \(m\) 个球,每次可将某个柱子顶部的球移至另一个没放满的柱子的顶部,求任意一个将同种颜色的球均放在同一个柱子上的方案数不超过 820000 的方案。
做法
70 pts
考虑将所有柱子的某种颜色的球都移至顶部,对于每个柱子需要另外两个一满一空的帮助。
在此基础上我们再加一点常数上的优化,每次只移动方案数最优的那个颜色,再加一点其他对不必要操作的减少,即可获得 70 pts,比较考验码力。
正解
我们考虑 \(n=2\) 的做法。
先算出第一个柱子颜色较少的那个的数量,将第二个柱子的同数量的球移到空柱,然后将第一个柱子的球分类,再将同一颜色的放回第一个柱子,再将第二个柱子的球也分了,这样就能解决了。操作次数为:\(5m-s\),其中 \(s\) 是第一个柱子中颜色较少的那个的数量。
然后我们考虑将这种做法扩展至 \(n>2\):
我们可以采用归并分治的思想,将颜色 \(\le mid\) 的看作 \(1\),将 \(>mid\) 的看作 \(2\)。那么,分治到最后,整个球也都分类完了。
计算方案数:
\[T(n)=2\times T(\frac{n}{2})+5\times m\times n
\]
\[T(n)=5\times m\times n\times logn
\]
可以通过本题!
具体实现就看代码吧!
code
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
inline int rd(){
int x=0,id=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')id=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-'0';
ch=getchar();
}
return x*id;
}
int n,m,a[55][405],ans[800003][2],top[55],tot;
bool fg[55];
inline void op(int x,int y){
ans[++tot][0]=x,ans[tot][1]=y,a[y][++top[y]]=a[x][top[x]--];
}
void sol(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
memset(fg,0,sizeof(fg));
for(int i=l;i<=mid;i++)//将其分成两种,小于等于mid,大于mid
for(int j=mid+1;j<=r;j++){
if(fg[i]||fg[j])continue;
int s=0;
for(int k=1;k<=m;k++)s+=(a[i][k]<=mid);
for(int k=1;k<=m;k++)s+=(a[j][k]<=mid);
if(s>=m){
s=0;
for(int k=1;k<=m;k++)s+=(a[i][k]<=mid);//一顿操作猛如虎
for(int k=1;k<=s;k++)op(j,n+1);
while(top[i])a[i][top[i]]<=mid?op(i,j):op(i,n+1);
for(int k=1;k<=s;k++)op(j,i);
for(int k=1;k<=m-s;k++)op(n+1,i);
for(int k=1;k<=m-s;k++)op(j,n+1);
for(int k=1;k<=m-s;k++)op(i,j);
while(top[n+1]){
if(top[i]==m||a[n+1][top[n+1]]>mid)op(n+1,j);
else op(n+1,i);
}
fg[i]=1;
}else{
s=0;
for(int k=1;k<=m;k++)s+=(a[j][k]>mid);//反向
for(int k=1;k<=s;k++)op(i,n+1);
while(top[j])a[j][top[j]]>mid?op(j,i):op(j,n+1);
for(int k=1;k<=s;k++)op(i,j);
for(int k=1;k<=m-s;k++)op(n+1,j);
for(int k=1;k<=m-s;k++)op(i,n+1);
for(int k=1;k<=m-s;k++)op(j,i);
while(top[n+1]){
if(top[j]==m||a[n+1][top[n+1]]<=mid)op(n+1,i);
else op(n+1,j);
}
fg[j]=1;
}
}
sol(l,mid);sol(mid+1,r);
}
int main(){
n=rd(),m=rd();
for(int i=1;i<=n;i++)
for(int j=1,x;j<=m;j++)x=rd(),a[i][++top[i]]=x;
sol(1,n);
cout<<tot<<"\n";
for(int i=1;i<=tot;i++)cout<<ans[i][0]<<" "<<ans[i][1]<<"\n";
return 0;
}

浙公网安备 33010602011771号