I.CF1423N BubbleSquare Tokens
神仙构造题。
首先,我们令所有点初始都没有放币,所有边上都放了一个币。则此时每个点的权值即为它的度数。
然后,我们考虑从小到大计算每个点的权值。对于每个点\(i\),我们枚举它所有相邻且编号比它小的点,假如该点上没有币,就把币从连接两点的边上移到另一端的点上。明显这样操作并不会改变该点的权值,只会减少当前点的权值。
这之后,上述所有点上都有了币。则我们可以选择将某些币移回边上,显然这只会单纯增加当前点的权值。假设当前点与\(k\)个点相邻,则它的权值可以被加上\([0,k]\)中任何一个数(取决于你究竟打算移回多少个币)。而这共\(k+1\)个值中,最多只有\(k\)个值被其余点占去,则当前点完全可以占住剩下那一个。直接使用一个哈希表维护有哪些值被占去即可。
时间复杂度\(O(k\log n)\)或\(O(k)\),取决于使用哪种哈希表。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt;
bool on[12510];
int sum[12510];
int val[1001000],s[1001000],t[1001000];
vector<int>v[12510];
set<int>hs;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d",&s[i],&t[i]),val[i]=1,sum[s[i]]++,sum[t[i]]++,v[s[i]].push_back(i),v[t[i]].push_back(i);
for(int i=1;i<=n;i++){
for(auto j:v[i]){
if((s[j]^t[j]^i)>i)continue;
if(!on[s[j]^t[j]^i])on[s[j]^t[j]^i]=true,val[j]--,sum[i]--;
hs.insert(sum[s[j]^t[j]^i]);
}
for(auto j:v[i]){
if((s[j]^t[j]^i)>i)continue;
if(hs.find(sum[i])==hs.end())break;
on[s[j]^t[j]^i]=false,val[j]++,sum[i]++;
}
hs.clear();
}
for(int i=1;i<=n;i++)cnt+=on[i];
printf("%d\n",cnt);
for(int i=1;i<=n;i++)if(on[i])printf("%d ",i);if(cnt)puts("");
for(int i=1;i<=m;i++)printf("%d %d %d\n",s[i],t[i],val[i]);
return 0;
}