zoukankan      html  css  js  c++  java
  • [NOIP2016] 天天爱跑步

    题意:

    传送门

    题解:

    去年D1T2,呵呵,被pei死,菜鸡的我考场上第一次做图论题,愉快爆零;

    链剖+转化等式;

    链剖就是求个lca和路径长度;

    首先考虑链的情况:

    先假设s在t的上面,那么对于一个点i,能对它产生贡献的路径一定满足i-s==w[i] && t>=i,于是我们设k[i]=i-w[i],cnt[i]表示以i为起点的路径条数,那么从上往下遍历,到点i先计算以点i为起点的贡献,再计算点i的答案,即访问cnt[k[i]],注意到t>=i,所以到i后cnt[以i为终点的起点]--,这里用vector存一下即可;

    对于s在t下面的类似,只是k[i]=deep[i]+w[i];

    正解和链很类似:

    考虑将一条路径划分为两条链:s->lca,t->lca,然后我们就可以用类似于链的做法;

    对于s->lca的一条链,我们只考虑s->t这条路径对s->lca这条链上的点的贡献,则对i有贡献需满足deep[s]-deep[i]w[i],移项之后又有k[i]=deep[i]+w[i]deep[s],于是在s上打+1标记,在lca上打-1标记 ,选1为根dfs,对每个点统计一下答案即可,但是有一点需要注意,与链的情况不同的是,这里的deep[s]是s的深度,可能会把别的子树的贡献计入答案,于是我们进入这个点时记一下这个点cnt[k[i]]的last,然后回溯后cnt[k[i]]-last就是对这个点产生的贡献。

    t->lca的情况类似,只是k[i]=deep[i]-w[i]==deep[t]-len(len为路径长度)。

    总结:对某个等式进行分析时,对等式进行化归,相关联的变量可划到等式的一边,从而找出各变量之间的线性关系。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #define ll long long
    #define N 300010
    #define M 300000
    using namespace std;
    
    int n,m,e_num,deep;
    int nxt[N<<1],to[N<<1],h[N];
    int fa[N],dep[N],top[N],siz[N],son[N],w[N],val[N],ans[N],cnt[N<<1];
    
    struct Node {int s,t,lca,len;}p[N];
    
    vector<int> v1[N],v2[N],v3[N];
    
    int gi() {
      int x=0,o=1; char ch=getchar();
      while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
      if(ch=='-') o=-1,ch=getchar();
      while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
      return o*x;
    }
    
    void add(int x, int y) {
      nxt[++e_num]=h[x],to[e_num]=y,h[x]=e_num;
    }
    
    void dfs1(int u) {
      siz[u]=1;
      for(int i=h[u]; i; i=nxt[i]) {
        int v=to[i];
        if(v==fa[u]) continue;
        fa[v]=u,dep[v]=dep[u]+1;
        dfs1(v);
        if(siz[v]>siz[son[u]]) son[u]=v;
        siz[u]+=siz[v];
      }
    }
    
    void dfs2(int u) {
      if(son[u]) top[son[u]]=top[u],dfs2(son[u]);
      for(int i=h[u]; i; i=nxt[i]) {
        int v=to[i];
        if(v==fa[u] || v==son[u]) continue;
        top[v]=v,dfs2(v);
      }
    }
    
    int lca(int x, int y) {
      while(top[x]!=top[y]) {
        if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
        else y=fa[top[y]];//hehe,chain divide was wrong
      }
      if(dep[x]<dep[y]) return x;
      else return y;
    }
    
    void sol1(int u) { 
      int last,num=dep[u]+w[u];
      if(num<=deep) last=cnt[num];
      for(int i=h[u]; i; i=nxt[i]) {
        if(to[i]!=fa[u]) sol1(to[i]);
      }
      cnt[dep[u]]+=val[u];
      if(num<=deep) ans[u]+=cnt[num]-last;
      for(int i=0; i<v1[u].size(); i++) cnt[v1[u][i]]--;
    }
    
    void sol2(int u) {
      int last,num=dep[u]-w[u]+M;
      last=cnt[num];
      for(int i=h[u]; i; i=nxt[i]) {
        if(to[i]!=fa[u]) sol2(to[i]);
      }
      for(int i=0; i<v2[u].size(); i++) cnt[v2[u][i]+M]++;
      ans[u]+=cnt[num]-last;
      for(int i=0; i<v3[u].size(); i++) cnt[v3[u][i]+M]--;
    }
    
    int main() {
      n=gi(),m=gi();
      for(int i=1; i<n; i++) {
        int x=gi(),y=gi();
        add(x,y),add(y,x);
      }
      fa[1]=1,dep[1]=1,top[1]=1;
      dfs1(1),dfs2(1);
      for(int i=1; i<=n; i++) w[i]=gi(),deep=max(deep,dep[i]);
      for(int i=1; i<=m; i++) {
        p[i].s=gi(),p[i].t=gi();
        p[i].lca=lca(p[i].s,p[i].t);
        p[i].len=dep[p[i].s]+dep[p[i].t]-2*dep[p[i].lca];    
        val[p[i].s]++;
        v1[p[i].lca].push_back(dep[p[i].s]);
        v2[p[i].t].push_back(dep[p[i].t]-p[i].len);
        v3[p[i].lca].push_back(dep[p[i].t]-p[i].len);
      }
      sol1(1);
      memset(cnt,0,sizeof(cnt));
      sol2(1);
      for(int i=1; i<=m; i++) {
        if(dep[p[i].s]==dep[p[i].lca]+w[p[i].lca]) ans[p[i].lca]--;
      }
      for(int i=1; i<=n; i++) printf("%d ", ans[i]);
      return 0;
    }
    
    

    附上部分分的代码,没有暴力跳路径和T=1的点

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define N 300010
    using namespace std;
    
    int n,m,e_num;
    int nxt[N<<1],to[N<<1],h[N],siz[N],dep[N],w[N],cnt[N],inx[N],v1[N],v2[100010][100],res[N];
    bool flg1=1,flg2=1,flg3=1,flg4=1,flg5=1;
    
    struct Node {int s,t;}p[N];
    
    int gi() {
      int x=0,o=1; char ch=getchar();
      while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
      if(ch=='-') o=-1,ch=getchar();
      while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
      return o*x;
    }
    
    void add(int x, int y) {
      nxt[++e_num]=h[x],to[e_num]=y,h[x]=e_num;
    }
    
    void dfs(int u, int fa) {
      for(int i=h[u]; i; i=nxt[i]) {
        int v=to[i];
        if(v==fa) continue;
        dep[v]=dep[u]+1;
        dfs(v,u);
        siz[u]+=siz[v];
      }
    }
    
    void work1() {
      for(int i=1; i<=m; i++) cnt[p[i].s]++;
      for(int i=1; i<=n; i++) {
        if(!w[i]) printf("%d ", cnt[i]);
        else printf("0 ");
      }
    }
    
    void work2() {
      for(int i=1; i<=m; i++) cnt[p[i].s]++;
      for(int i=1; i<=n; i++) {
        printf("%d ", cnt[i]);
      }
    }
    
    void work3() {
      for(int i=1; i<=m; i++) {
        if(p[i].s<=p[i].t) {
          v1[p[i].s]++,v2[p[i].t][++v2[p[i].t][0]]=p[i].s;
        }
      }
      for(int i=1; i<=n; i++) {
        cnt[i]+=v1[i];
        if(i-w[i]>=1) res[i]+=cnt[i-w[i]];
        for(int j=1; j<=v2[i][0]; j++) cnt[v2[i][j]]--;
      }
      memset(v1,0,sizeof(v1));
      memset(v2,0,sizeof(v2));
      memset(cnt,0,sizeof(cnt));
      for(int i=1; i<=m; i++) {
        if(p[i].s>p[i].t)
          v1[p[i].s]++,v2[p[i].t][++v2[p[i].t][0]]=p[i].s;
      }
      for(int i=n; i>=1; i--) {
        cnt[i]+=v1[i];
        if(i+w[i]<=n) res[i]+=cnt[i+w[i]];
        for(int j=1; j<=v2[i][0]; j++) cnt[v2[i][j]]--;
      }
      for(int i=1; i<=n; i++)
        printf("%d ", res[i]);
    }
    
    void work4() {
      for(int i=1; i<=m; i++) siz[p[i].t]++;
      dfs(1,0);
      for(int i=1; i<=n; i++) {
        if(dep[i]==w[i]) cnt[i]=siz[i];
        printf("%d ", cnt[i]);
      }
    }
    
    int main() {
      n=gi(),m=gi();
      for(int i=1; i<n; i++) {
        int x=gi(),y=gi();
        add(x,y),add(y,x);
        inx[x]++,inx[y]++;
        if(inx[x]>2 || inx[y]>2) flg3=0;
      }
      for(int i=1; i<=n; i++) {
        w[i]=gi();
        if(w[i]) flg2=0;
      }
      for(int i=1; i<=m; i++) {
        int s=gi(),t=gi();
        if(s!=t) flg1=0;
        if(s!=1) flg4=0;
        if(t!=1) flg5=0;
        p[i]=(Node){s,t};
      }
      if(flg1) work1();
      if(flg2) work2();
      if(flg3) work3();
      if(flg4) work4();
      return 0;
    }
    
  • 相关阅读:
    IsPostBack
    判断客户端.net版本
    js 汉字转换成拼音 转载
    观察者模式
    常用的js阻止冒泡的方法
    jquery中事件的绑定
    uclinux编译 skyeye运行
    dotNet学习之路 Struct与Class异同点
    dotNet学习之路 Delegate内部原理
    设计模式之旅(策略模式) 十号刚发工资的博友们,赶紧跟我一起算算你们的老板有没有给你少发工资。。。
  • 原文地址:https://www.cnblogs.com/HLXZZ/p/7717597.html
Copyright © 2011-2022 走看看