Codeforces 134 C. Swaps

問題

n人の人が自分の色のカードa[i]枚ずつ持っている。
次のような条件を満たす「交換」を何度か行い、各人が持っているカードが全て自分以外の色になっているようにしたい。
それが可能であれば、Yesと、その交換手順を具体的に一通り出力し、不可能ならNoを出力せよ。

  • 交換は二人が行う
  • お互いに、自分の色のカードを差し出し、自分のまだ持っていない色のカードを貰う

制約条件

n≦200000
カードの合計枚数≦2000000

方針

greedy.
ある人のカードに注目したとき、そのカードは全部別の人へわたらなければならないので、
その人のカードの枚数だけ別の人を選ぶ必要がある。
これは、カードを持っている枚数が多い人から順に選べばいい。


これを繰り返せばよい。


多いカードを持っている人を探すのを、配列を毎回ソートしてやるなどするとTLE.
カードの合計枚数が少ないことに注目し、priority_queueを使うとよい。
priority_queueに要素が出し入れされる回数は、カードの交換回数*2に等しいので、間に合う。

ソースコード

int n,s;
pi c[200000];
vector<pi> ans;

bool solve(){
	priority_queue<pi> q;
	rep(i,n)if(c[i].first>0)q.push(c[i]);
	rep(i,n-1){
		vector<pi> tmp;
		if(q.empty())return 1;
		pi p=q.top(); q.pop();
		if(q.size()<p.first)return 0;
		rep(j,p.first){
			pi r=q.top(); q.pop();
			ans.pb(mp(p.second+1,r.second+1));
			if(--r.first>0)tmp.pb(r);
		}
		rep(j,tmp.size())q.push(tmp[j]);
	}
	return ans.size()*2==s;
}
void run()
{
	cin>>n>>s;
	rep(i,n){
		int t; cin>>t;
		c[i]=mp(t,i);
	}
	if(solve()){
		cout<<"Yes"<<endl;
		cout<<ans.size()<<endl;
		rep(i,ans.size())cout<<ans[i].first<<" "<<ans[i].second<<endl;
	}
	else cout<<"No"<<endl;
}