2022.7.1 CF1033E&CF516D

CF1033E

一个 new trick 就是判断二分图先找到一棵生成树,然后判断奇点之间或者偶点之间有无边即可。

我们可以 bfs 地建树,每个点往外拓展使用类似线段树的形式,由于只会拓展 \(n-1\) 条边,所以此处复杂度是 \(n\log n\) 的。

然后判断,如果是二分图的话直接输出答案了,否则我们类似之前还是用线段树的形式找出一条边就好。

代码比想象中的要冗长,也很难调/ll

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
const int N=605;
#define vi vector<int>
#define pb push_back
vi all,q1,q2,qry,G[N],wa;
int n,m,col[N],vis[N];
inline void getall(){
	all.clear();
	for(int i=1;i<=n;i++)
		if(!vis[i])all.pb(i);
}
queue<int>Q;
inline int query(){
	if(qry.size()<=1)return 0;
	printf("? %d\n",qry.size());
	for(auto x:qry)
		printf("%d ",x);puts("");
	fflush(stdout);
	int S=read();return S;
}
inline int Query(){
	int S=0,S1=0,S2=0;
	qry.clear();
	for(auto x:q1)qry.pb(x);
	S1=query();qry.clear();
	for(auto x:q2)qry.pb(x);
	S2=query();
	for(auto x:q1)qry.pb(x);
	S=query();
	return S-S1-S2;
}
inline void Link(int id,int p){
	G[id].pb(p);
	col[p]=col[id]^1;vis[p]=1;Q.push(p);
}
inline void link(int id,int l,int r){
	q1.clear(),q2.clear();
	q1.pb(id);
	for(int i=l;i<=r;i++)q2.pb(all[i]);
	if(!Query())return;
	if(l==r)return void(Link(id,all[l]));
	int mid=(l+r)>>1;
	link(id,l,mid);link(id,mid+1,r);
}
int p1,p2;
inline int loc2(int l,int r,int l2,int r2,int S2){
	if(l==r)return l;
	int mid=(l+r)>>1;qry.clear();
	for(int i=l;i<=mid;i++)qry.pb(wa[i]);
	int S1=query();
	for(int i=l2;i<=r2;i++)qry.pb(wa[i]);
	int S=query();
	if(S>S1+S2)return loc2(l,mid,l2,r2,S2);
	else return loc2(mid+1,r,l2,r2,S2);
} 
inline void loc1(int l,int r,int S){
	int mid=(l+r)>>1,S1,S2;
	qry.clear();
	for(int i=l;i<=mid;i++)qry.pb(wa[i]);
	S1=query();qry.clear();
	for(int i=mid+1;i<=r;i++)qry.pb(wa[i]);
	S2=query();
	if(S>S1+S2){
		p1=loc2(l,mid,mid+1,r,S2);
		p2=loc2(mid+1,r,p1,p1,0);
		p1=wa[p1],p2=wa[p2];
	}else if(S1)loc1(l,mid,S1);
	else loc1(mid+1,r,S2);
}
int dep[N],F[N];
inline void dfs(int x,int fa){
	dep[x]=dep[fa]+1;F[x]=fa;
	for(auto t:G[x])dfs(t,x);
}
vi R1,R2;
inline void solve(){
	for(auto x:qry)wa.pb(x);
	int S=query();
	loc1(0,wa.size()-1,S);
	dfs(1,0);if(dep[p1]<dep[p2])swap(p1,p2);
	int t1=p1,t2=p2;
	while(dep[p1]>dep[p2])
		R1.pb(p1),p1=F[p1];
	while(p1!=p2)R1.pb(p1),R2.pb(p2),p1=F[p1],p2=F[p2];
	int len=R1.size()+R2.size()+1;
	printf("N %d\n",len);
	printf("%d ",p1);reverse(R1.begin(),R1.end());
	for(auto x:R1)printf("%d ",x);
	for(auto x:R2)printf("%d ",x);
	
	fflush(stdout);exit(0);
}
int main(){
	n=read();vis[1]=1;
	getall();Q.push(1);
	while(!Q.empty()&&!all.empty()){
		int x=Q.front();Q.pop();
		link(x,0,all.size()-1);
		getall();
	}qry.clear();
	for(int i=1;i<=n;i++)if(!col[i])qry.pb(i);
	if(query())solve();qry.clear();
	for(int i=1;i<=n;i++)if(col[i])qry.pb(i);
	if(query())solve();qry.clear();
	for(int i=1;i<=n;i++)if(col[i])qry.pb(i);
	printf("Y %d\n",qry.size());
	for(auto x:qry)printf("%d ",x);puts("");
	return 0;
}

CF516D

这种树上最远一看就是直径嘛,我们求出直径即求出所有 \(f\) 值。此时我们把最小的 \(f\) 提为根,形成一个堆,每次询问二分出最远的满足条件的祖先,链加即可。

其实我自己的想法:主席树好像复杂度也是正确的,但是貌似难写亿点点然后常数也有亿点点大。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,m,beg[maxn],nex[maxn],to[maxn],w[maxn],e,p1,p2;
inline void add(int x,int y,int z){
	e++;nex[e]=beg[x];beg[x]=e;to[e]=y;w[e]=z;
	e++;nex[e]=beg[y];beg[y]=e;to[e]=x;w[e]=z;
}
int Mx1[maxn],Mx2[maxn],id1[maxn],id2[maxn],ans;
int dep[maxn],dis[maxn],F[maxn][20],f[maxn],rt;
inline void dfs(int x,int fa){
	Mx1[x]=0;id1[x]=x;F[x][0]=fa;
	dep[x]=dep[fa]+1;
	for(int i=1;i<=19;i++)
		F[x][i]=F[F[x][i-1]][i-1];
	for(int i=beg[x];i;i=nex[i]){
		int t=to[i];
		if(t==fa)continue;
		dis[t]=dis[x]+w[i];dfs(t,x);
		if(Mx1[t]+w[i]>Mx1[x]){
			Mx2[x]=Mx1[x];id2[x]=id1[x];
			Mx1[x]=Mx1[t]+w[i];id1[x]=id1[t];
		}else if(Mx1[t]+w[i]>Mx2[x])
			Mx2[x]=Mx1[t]+w[i],id2[x]=id1[t];
	}if(Mx1[x]+Mx2[x]>ans)p1=id1[x],p2=id2[x],ans=Mx1[x]+Mx2[x];
}
inline int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=19;~i;--i)
		if(dep[F[x][i]]>=dep[y])x=F[x][i];
	if(x==y)return x;
	for(int i=19;~i;--i)
		if(F[x][i]^F[y][i])x=F[x][i],y=F[y][i];
	return F[x][0];
}
inline int Dis(int x,int y){
	return dis[x]+dis[y]-2*dis[lca(x,y)];
}
int L,cnt[maxn];
inline void calc(int x,int fa){
	int anc=x;
	for(int i=19;~i;--i)
		if(F[anc][i]&&f[F[anc][i]]+L>=f[x])anc=F[anc][i];
	for(int i=beg[x];i;i=nex[i])
		if(to[i]^fa)calc(to[i],x),cnt[x]+=cnt[to[i]];
	cnt[x]++,cnt[F[anc][0]]--;
}
inline void solve(){
	L=read();
	memset(cnt,0,sizeof(cnt));
	calc(rt,0);int Mx=0;
	for(int i=1;i<=n;i++)
		Mx=max(Mx,cnt[i]);
	printf("%lld\n",Mx);
}
signed main(){
	n=read();
	for(int i=1,x,y,z;i<n;i++)
		x=read(),y=read(),z=read(),add(x,y,z);
	dfs(1,0);
	for(int i=1;i<=n;i++)
		f[i]=max(Dis(i,p1),Dis(i,p2));
	for(int i=1;i<=n;i++)
		if(!rt||f[i]<f[rt])rt=i;
	memset(F,0,sizeof(F));
	dfs(rt,0);
    m=read();
    while(m--)solve();
	return 0;
}
posted @ 2022-07-02 18:54  syzf2222  阅读(35)  评论(0编辑  收藏  举报