[USACO18JAN]Cow at Large P

XIII.[USACO18JAN]Cow at Large P

这题我做的时候时限1s,然后卡不过去……之后不得不发帖请求把时限调大到题面中的4s

假设当前询问了点rt,那么我们把这棵树变成以rt为根,设depi为此刻i节点的深度。

我们再令fi表示i节点距离最近的叶子的距离(这个可以通过二次扫描与换根法通过DP求出,不是重点,就跳过了)。则如果一个节点有depifi,则这个点便可以被那个叶子看住。

显然,一个叶子如果放了农民,被看住的一定是一棵子树,而子树的树根即为最高的那个具有depifi的点。这也意味着所有最高的符合条件的点,都代表了一棵放了农民的子树,也即农民的数量。

我们再次审视这个条件,发现它即为所有满足depifi  depfai<ffai的点的数量。

因为前一半一定是对一棵子树全部成立,因此我们考虑推出某种式子,使得一整棵子树的该式之和为一。我们考虑从度数下手。

若设degxx节点的度数的话,则对于一棵常规的树,degx=2(n1)应是成立的,因为每条边会被统计两次。

但是我们现在子树的根节点的度数还包含连向它父亲的那条边,因此有degx=2n1。当然,这个特例在树根处无效——但是如果树根都符合条件,则树根只有可能是叶子。因此叶子节点特判答案为1即可。

我们可以将degx=2n1变化成2degx=1,这就是我们之前想要的那个和为一的式子。

我们还要满足depifi这一条件。因此现在答案即为depifi2degi

这个式子就可以用点分治求解了。我们将depi拆成两部分,即dis(lca,i)dis(lca,rt)。则我们现在即可将式子表示成dis(lca,i)+dis(lca,rt)fi。将可以预处理的dis(lca,i)移过去,便得到dis(lca,rt)fidis(lca,i)。将式子右边与2degxmake_pair压入vector<pair<int,int> >并排序即可通过前缀和+二分求出所有满足条件的点的2degx的和。

复杂度O(nlog2n)

建出点分治树然后处理的代码:

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
int n,fa[200100],dep[200100],in[200100],tot,mn[400100][20],LG[400100],f[200100];
namespace Tree{
    vector<int>v[200100];
    int sz[200100],SZ,msz[200100],ROOT;
    bool vis[200100];
    void getsz(int x,int fa){
        sz[x]=1;
        for(auto y:v[x])if(!vis[y]&&y!=fa)getsz(y,x),sz[x]+=sz[y];
    }
    void getroot(int x,int fa){
        sz[x]=1,msz[x]=0;
        for(auto y:v[x])if(!vis[y]&&y!=fa)getroot(y,x),sz[x]+=sz[y],msz[x]=max(msz[x],sz[y]);
        msz[x]=max(msz[x],SZ-sz[x]);
        if(msz[x]<msz[ROOT])ROOT=x;
    }
    void solve(int x){
        getsz(x,0); 
        vis[x]=true;
        for(auto y:v[x]){
            if(vis[y])continue;
            ROOT=0,SZ=sz[y],getroot(y,0),fa[ROOT]=x,solve(ROOT);
		}
	}
    void getural(int x,int fa){
		mn[++tot][0]=x,in[x]=tot;
		for(auto y:v[x])if(y!=fa)dep[y]=dep[x]+1,getural(y,x),mn[++tot][0]=x;
    }
    void getdfsI(int x,int fa){
    	f[x]=(v[x].size()==1?0:0x3f3f3f3f);
    	for(auto y:v[x])if(y!=fa)getdfsI(y,x),f[x]=min(f[x],f[y]+1);
	}
	void getdfsII(int x,int fa){
		for(auto y:v[x])if(y!=fa)f[y]=min(f[y],f[x]+1),getdfsII(y,x);
	}
}
int MIN(int i,int j){
    return dep[i]<dep[j]?i:j;
}
int LCA(int i,int j){
	if(i>j)swap(i,j);
	int k=LG[j-i+1];
	return MIN(mn[i][k],mn[j-(1<<k)+1][k]);
}
int DIS(int i,int j){
	return dep[i]+dep[j]-dep[LCA(in[i],in[j])]*2;
}
namespace cdt{
	vector<pair<int,int> >sf[200100],pa[200100];
	void init(int x){
		int val=2-Tree::v[x].size();
		for(int u=x;u;u=fa[u]){
			sf[u].push_back(make_pair(f[x]-DIS(x,u),val));
			if(fa[u])pa[u].push_back(make_pair(f[x]-DIS(x,fa[u]),val));
		}
	}
	void prep(){
		for(int i=1;i<=n;i++)init(i);
		for(int i=1;i<=n;i++){
			sf[i].push_back(make_pair(0x80808080,0));
			pa[i].push_back(make_pair(0x80808080,0));
			sort(sf[i].begin(),sf[i].end()),sort(pa[i].begin(),pa[i].end());
//			printf("%d:\n",i);
//			for(int j=1;j<sf[i].size();j++)printf("(%d,%d)",sf[i][j].first,sf[i][j].second);puts("");
//			for(int j=1;j<pa[i].size();j++)printf("(%d,%d)",pa[i][j].first,pa[i][j].second);puts("");
			for(int j=1;j<sf[i].size();j++)sf[i][j].second+=sf[i][j-1].second;
			for(int j=1;j<pa[i].size();j++)pa[i][j].second+=pa[i][j-1].second;
		}
	}
	int ask(int x){
		if(f[x]==0)return 1;
		int res=0;
		for(int u=x;u;u=fa[u]){
			int p=upper_bound(sf[u].begin(),sf[u].end(),make_pair(DIS(x,u),(int)0x3f3f3f3f))-sf[u].begin()-1;
			res+=sf[u][p].second;
			if(!fa[u])break;
			p=upper_bound(pa[u].begin(),pa[u].end(),make_pair(DIS(x,fa[u]),(int)0x3f3f3f3f))-pa[u].begin()-1;
			res-=pa[u][p].second;
		}
		return res;
	}
}
void read(int &x){
	x=0;
	char c=getchar();
	while(c>'9'||c<'0')c=getchar();
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
int main(){
	read(n); 
	for(int i=1,x,y;i<n;i++)read(x),read(y),Tree::v[x].push_back(y),Tree::v[y].push_back(x);
	Tree::msz[0]=0x3f3f3f3f,Tree::SZ=n,Tree::getroot(1,0),Tree::solve(Tree::ROOT);
	Tree::getdfsI(1,0),Tree::getdfsII(1,0);
	Tree::getural(1,0);
	for(int i=2;i<=tot;i++)LG[i]=LG[i>>1]+1;
	for(int j=1;j<=LG[tot];j++)for(int i=1;i+(1<<j)-1<=tot;i++)mn[i][j]=MIN(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
	cdt::prep();
	for(int i=1;i<=n;i++)printf("%d\n",cdt::ask(i));
	return 0;
}

没建点分治树直接点分治的代码:

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
int n,dep[200100],f[200100],res[200100];
vector<int>v[200100];
int sz[200100],SZ,msz[200100],ROOT;
bool vis[200100];
void getdfsI(int x,int fa){
	f[x]=(v[x].size()==1?0:0x3f3f3f3f);
	for(auto y:v[x])if(y!=fa)getdfsI(y,x),f[x]=min(f[x],f[y]+1);
}
void getdfsII(int x,int fa){for(auto y:v[x])if(y!=fa)f[y]=min(f[y],f[x]+1),getdfsII(y,x);}
void getszdep(int x,int fa){
    sz[x]=1;
    for(auto y:v[x])if(!vis[y]&&y!=fa)dep[y]=dep[x]+1,getszdep(y,x),sz[x]+=sz[y];
}
void getroot(int x,int fa){
    sz[x]=1,msz[x]=0;
    for(auto y:v[x])if(!vis[y]&&y!=fa)getroot(y,x),sz[x]+=sz[y],msz[x]=max(msz[x],sz[y]);
    msz[x]=max(msz[x],SZ-sz[x]);
    if(msz[x]<msz[ROOT])ROOT=x;
}
vector<pair<int,int> >vp;
void getwrite(int x,int fa){
	vp.push_back(make_pair(f[x]-dep[x],2-v[x].size()));
	for(auto y:v[x])if(!vis[y]&&y!=fa)getwrite(y,x);
}
void getread(int x,int fa,int k){
	int p=upper_bound(vp.begin(),vp.end(),make_pair(dep[x],(int)0x3f3f3f3f))-vp.begin()-1;
	res[x]+=k*vp[p].second;
	for(auto y:v[x])if(!vis[y]&&y!=fa)getread(y,x,k);
}
void calc(int x){
	vp.clear(),vp.push_back(make_pair(0x80808080,0)),getwrite(x,0),sort(vp.begin(),vp.end());
	for(int i=1;i<vp.size();i++)vp[i].second+=vp[i-1].second;
	getread(x,0,1);
	for(auto y:v[x]){
		if(vis[y])continue;
		vp.clear(),vp.push_back(make_pair(0x80808080,0)),getwrite(y,x),sort(vp.begin(),vp.end());
		for(int i=1;i<vp.size();i++)vp[i].second+=vp[i-1].second;
		getread(y,x,-1);
	}
}
void solve(int x){
    dep[x]=0,getszdep(x,0); 
	calc(x);
    vis[x]=true;
    for(auto y:v[x]){
        if(vis[y])continue;
        ROOT=0,SZ=sz[y],getroot(y,0),solve(ROOT);
	}
}
void read(int &x){
	x=0;
	char c=getchar();
	while(c>'9'||c<'0')c=getchar();
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
int main(){
	read(n); 
	for(int i=1,x,y;i<n;i++)read(x),read(y),v[x].push_back(y),v[y].push_back(x);
	getdfsI(1,0),getdfsII(1,0);
	msz[0]=0x3f3f3f3f,SZ=n,getroot(1,0),solve(ROOT);
	for(int i=1;i<=n;i++)printf("%d\n",v[i].size()==1?1:res[i]);
	return 0;
}

posted @   Troverld  阅读(68)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示