Luogu P1600 [NOIp提高组2016]天天爱跑步


题目描述 传送门


我觉得好难好难啊,看题解我都看不懂(我觉得实现起来有点麻烦)。
总结一下:
首先也许可以想出S=1和T=1的数据的做法,然后想到把S->T路径拆成S->LCA和LCA->T.
然后要想到一个点i可观测到的人一定满足:
S->LCA路径:
depth[i]+w[i]=depth[S]
LCA->T路径:
distance(S,i)=w[i]
depth[S]+depth[i]2depth[LCA]=w[i]
整理得: depth[i]w[i]=depth[T]distance(S,T)
等式右边的都是定值,可以预处理好。
然后两遍DFS分别处理S->LCA和LCA->T的情况,并用桶记录有多少个满足条件的等式右边。
注意到对于一个点如果一条路径没有经过它即使满足等式也不行。

  • 如果那条路径不在以那个点位根的子树,解决方法是ans累加桶的变化量。
  • 如果那条路径在,解决方法是在DFS回溯时减去已这个点为LCA的路径的贡献。

然而这样还会重复算到每条路径的LCA的答案(因为LCA既在S->LCA又在LCA->T)所以最后减去重复的才是答案。


//代码参考dalao的
#include
#include
#include
#include
#include
using namespace std;
const int maxn=3e5+5,maxm=3e5+5,N=3e5;
int maxd=0;
struct Edge{
    int to,nxt;
    Edge(int a=0,int b=0):to(a),nxt(b){}
}edge[maxm*2];
int n,m; 
int s[maxn],t[maxn],lca[maxn],len[maxn]; 
int cnt=0,h[maxn];
int f[maxn],depth[maxn],son[maxn],size[maxn],top[maxn]; 
int w[maxn],tong[maxn*2],tong2[maxn*2],num[maxn],ans[maxn];
vector<int> v[maxn],v2[maxn],v3[maxn]; 
void addedge(int from,int to){
    edge[++cnt]=Edge(to,h[from]);
    h[from]=cnt;
}
void dfs1(int u,int fa){
    f[u]=fa;
    depth[u]=depth[fa]+1;
    son[u]=0;
    size[u]=1;
    for(int i=h[u];i;i=edge[i].nxt) if(edge[i].to!=fa) {
        int v=edge[i].to;
        dfs1(v,u);
        if(size[v]>size[son[u]]) son[u]=v;
        size[u]+=size[v];
    }
}
void dfs2(int u,int tp){
    top[u]=tp;
    if(size[u]>1) dfs2(son[u],tp);
    for(int i=h[u];i;i=edge[i].nxt) if(edge[i].to!=f[u]&&edge[i].to!=son[u])
        dfs2(edge[i].to,edge[i].to);
}
int get_lca(int x,int y){   //我用树链剖分求的LCA
    int f1=top[x],f2=top[y];
    while(f1!=f2){
            if(depth[f1]if(depth[x]return y;
} 
void dfss(int u,int fa){
    int now=depth[u]+w[u],x;
    if(nowfor(int i=h[u];i;i=edge[i].nxt)if(edge[i].to!=fa){
        dfss(edge[i].to,u);
    }
    tong[depth[u]]+=num[u]; 
    if(nowfor(int i=0;ivoid dfst(int u,int fa){
    int now=depth[u]-w[u]+N;
    int x=tong2[now];
    for(int i=h[u];i;i=edge[i].nxt)if(edge[i].to!=fa){
        dfst(edge[i].to,u);
    }
    for(int i=0;ifor(int i=0;iint main(){
    memset(h,0,sizeof(h));
    memset(num,0,sizeof(num));
    cin>>n>>m;
    for(int i=0;i1;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        addedge(a,b);
        addedge(b,a);
    }
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    depth[1]=1;
    son[0]=0;
    dfs1(1,1);
    dfs2(1,1);
    for(int i=1;i<=n;i++) maxd=max(maxd,depth[i]);
    maxd++;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&s[i],&t[i]);
        lca[i]=get_lca(s[i],t[i]);
        len[i]=depth[s[i]]+depth[t[i]]-depth[lca[i]]*2;
        v[lca[i]].push_back(depth[s[i]]);
        num[s[i]]++;
    }
    dfss(1,1);
    for(int i=1;i<=m;i++){
        v2[t[i]].push_back(depth[t[i]]-len[i]);
        v3[lca[i]].push_back(depth[t[i]]-len[i]);
    }
    dfst(1,1);
    for(int i=1;i<=m;i++) if(depth[s[i]]-depth[lca[i]]==w[lca[i]]) ans[lca[i]]--;
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

一开始求LCA还写错了QWQ


NOIp提高组2016 AK

你可能感兴趣的:(Luogu,NOIp,LCA,概率-期望)