CF1423N BubbleSquare Tokens 题解

发布时间 2023-10-10 16:27:41作者: xuantianhao

BubbleSquare Tokens

神仙构造题。

首先,我们令所有点初始都没有放币,所有边上都放了一个币。则此时每个点的权值即为它的度数。

然后,我们考虑从小到大计算每个点的权值。对于每个点 \(i\),我们枚举它所有相邻且编号比它小的点,假如该点上没有币,就把币从连接两点的边上移到另一端的点上。明显这样操作并不会改变该点的权值,只会减少当前点的权值。

这之后,上述所有点上都有了币。则我们可以选择将某些币移回边上,显然这只会单纯增加当前点的权值。假设当前点与 \(k\) 个点相邻,则它的权值可以被加上 \([0,k]\) 中任何一个数(取决于你究竟打算移回多少个币)。而这共 \(k+1\) 个值中,最多只有 \(k\) 个值被其余点占去,则当前点完全可以占住剩下那一个。直接使用一个哈希表维护有哪些值被占去即可。

时间复杂度 \(O(k \log n)\)\(O(k)\),取决于使用哪种哈希表。

#include<bits/stdc++.h>
using namespace std;
const int N=13000;
const int M=1e6+100;
struct node{
	int s,t,val;
}e[M];
int n,k,cnt;
bool vis[N];
int sum[N];
vector<int>edge[N];
set<int>S;
inline int read(){
    char c=getchar();int f=1,x=0;
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int main(){
    n=read();
    k=read();
    for(int i=1;i<=k;i++){
		e[i].s=read();e[i].t=read();
		e[i].val=1;
		sum[e[i].s]++;
		sum[e[i].t]++;
		edge[e[i].s].push_back(i);
		edge[e[i].t].push_back(i);
	}
    for(int i=1;i<=n;i++){
        for(auto j:edge[i]){
        	int tmp=e[j].s^e[j].t^i;
            if(tmp>i) continue;
            if(!vis[tmp]){
				vis[tmp]=1;
				e[j].val--;
				sum[i]--;
			}
            S.insert(sum[tmp]); 
        }
        for(auto j:edge[i]){
        	int tmp=e[j].s^e[j].t^i;
            if(tmp>i) continue;
            if(S.find(sum[i])==S.end()) break;
            vis[tmp]=0;
			e[j].val++;
			sum[i]++;
        }
        S.clear();
    }
    for(int i=1;i<=n;i++) cnt+=vis[i];
    printf("%d\n",cnt);
    for(int i=1;i<=n;i++) if(vis[i]) printf("%d ",i);
	if(cnt) printf("\n");
    for(int i=1;i<=k;i++) printf("%d %d %d\n",e[i].s,e[i].t,e[i].val);
    return 0;
}