[USACO18JAN]Cow at Large P
XIII.[USACO18JAN]Cow at Large P
这题我做的时候时限1s,然后卡不过去……之后不得不发帖请求把时限调大到题面中的4s
假设当前询问了点,那么我们把这棵树变成以为根,设为此刻节点的深度。
我们再令表示节点距离最近的叶子的距离(这个可以通过二次扫描与换根法通过DP求出,不是重点,就跳过了)。则如果一个节点有,则这个点便可以被那个叶子看住。
显然,一个叶子如果放了农民,被看住的一定是一棵子树,而子树的树根即为最高的那个具有的点。这也意味着所有最高的符合条件的点,都代表了一棵放了农民的子树,也即农民的数量。
我们再次审视这个条件,发现它即为所有满足的点的数量。
因为前一半一定是对一棵子树全部成立,因此我们考虑推出某种式子,使得一整棵子树的该式之和为一。我们考虑从度数下手。
若设为节点的度数的话,则对于一棵常规的树,应是成立的,因为每条边会被统计两次。
但是我们现在子树的根节点的度数还包含连向它父亲的那条边,因此有。当然,这个特例在树根处无效——但是如果树根都符合条件,则树根只有可能是叶子。因此叶子节点特判答案为即可。
我们可以将变化成,这就是我们之前想要的那个和为一的式子。
我们还要满足这一条件。因此现在答案即为。
这个式子就可以用点分治求解了。我们将拆成两部分,即与。则我们现在即可将式子表示成。将可以预处理的移过去,便得到。将式子右边与make_pair
压入vector<pair<int,int> >
并排序即可通过前缀和+二分求出所有满足条件的点的的和。
复杂度。
建出点分治树然后处理的代码:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?