Codeforces 1423C - Dušan's Railway(树分块)

首先 \(k>3\)\(k=3\) 做,也就是说题目等价于找不超过 \(10n\) 条路径使得任意两点间的路径都可以表示为选定的这些路径中不相交的三者的并。

先考虑链怎么做,关于这个 \(3\),很自然的想法是取若干关键点,关键点之间两两连边,其余点再像相邻两关键点连边,因此考虑分块,每 \(B\) 个点设个关键点,这样不同块内的点就 ok 了,但是同一块内的点还不符合条件。比较蠢的想法是令 \(B=n^{1/3}\) 然后块内两两连边,这样边数大概是 \(n^{4/3}\)。但是其实块内也是一个结构类似的子问题,可以递归处理。由于一个数最多开方 \(\log\log n\) 次就会变成 \(1\),因此直接令 \(B=\sqrt{n}\) 然后递归处理复杂度大概可以达到 \(n\log\log n\)

树的情况也是类似的,直接上树分块即可。我是按照 Kubic 题解里随机撒点然后建虚树的做法来写的,其实一直不太理解这个写法为啥不会被卡掉,所以前几天特地去 u 群问了一圈,然后 skip2004 说了一些我听不懂的东西,说划分出来的连通块大小的 \(\max\) 期望是 sqrt log 的,因此实际上撒的点数要略多余 \(\sqrt{n}\)。不过作为 oier,其实我们不必特别在意这个差异,一般出题人也不会可以造出卡到期望上界的数据。

const int MAXN=10000;
mt19937 rng(114514);
int n,k,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
vector<pii>res;
int typ[MAXN+5],vis[MAXN+5];
queue<vector<int> >q;
pair<vector<int>,int>dfs(int x,int f){
	vector<pair<vector<int>,int> >v;int sum=0;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];if(y==f||!typ[y])continue;
		v.pb(dfs(y,x));sum+=(v.back().se>0);
	}
	if(typ[x]==2||sum>=2){
		for(auto t:v){
			q.push(t.fi);
			for(int z:t.fi){
				if(t.se)res.pb(mp(z,t.se));
				res.pb(mp(z,x));
			}
		}typ[x]=2;return mp(vector<int>{},x);
	}else{
		int pt=0;vector<int>tot;for(auto t:v)pt|=t.se;
		for(auto t:v)for(int y:t.fi)tot.pb(y);tot.pb(x);
		return mp(tot,pt);
	}
}
void work(vector<int>v){
	if(v.size()<=1)return;
	memset(typ,0,sizeof(typ));memset(vis,0,sizeof(vis));
	for(int x:v)typ[x]=1;shuffle(v.begin(),v.end(),rng);
	int lim=(int)sqrt(v.size());for(int i=0;i<lim;i++)typ[v[i]]=2;
//	printf("work {");for(int i=0;i<v.size();i++)printf("%d%c",v[i],",}"[i+1==v.size()]);printf("\n");
//	for(int i=0;i<lim;i++)printf("%d ",v[i]);printf("\n");
	while(!q.empty())q.pop();dfs(v[0],0);vector<int>nd;
	for(int x:v)if(typ[x]==2)nd.pb(x);
	for(int i=0;i<nd.size();i++)for(int j=i+1;j<nd.size();j++)
		res.pb(mp(nd[i],nd[j])); 
	vector<vector<int> >tmp;
	while(!q.empty())tmp.pb(q.front()),q.pop();
	for(vector<int>vv:tmp)work(vv);
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
	vector<int>vec;for(int i=1;i<=n;i++)vec.pb(i);work(vec);
	printf("%d\n",(int)res.size());
	for(pii p:res)printf("%d %d\n",p.fi,p.se);
	return 0;
}
posted @ 2023-05-16 15:44  tzc_wk  阅读(51)  评论(0编辑  收藏  举报