zoukankan      html  css  js  c++  java
  • 2017.10.24

    今天主要进行了一些模板的复习。主要是树剖lca方面。感觉虽然是学过的东西,不写的话还是很容易忘的。

    商务旅行 


     

     时间限制: 1 s
     空间限制: 128000 KB
     
     
    题目描述 Description

    某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。

    假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路,在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。

    你的任务是帮助该商人计算一下他的最短旅行时间。

    输入描述 Input Description

    输入文件中的第一行有一个整数N,1<=n<=30 000,为城镇的数目。下面N-1行,每行由两个整数a 和b (1<=ab<=n; a<>b)组成,表示城镇a和城镇b有公路连接。在第N+1行为一个整数M,下面的M行,每行有该商人需要顺次经过的各城镇编号。

    输出描述 Output Description

        在输出文件中输出该商人旅行的最短时间。

    样例输入 Sample Input
    5
    1 2
    1 5
    3 5
    4 5
    4
    1
    3
    2
    5
    样例输出 Sample Output

    7

    挺裸的lca问题。多的不解释。以前用tarjan写了,今天树剖打了一遍。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n,u,v,son[100010],fa[100010],top[100010],d[100010],head[100010],vis[100010],siz[100010],cnt,m,a,b,ans;
    struct edge{
        int v,next;
    }E[150010];
    void add(int u,int v){
        E[++cnt].v=v;
        E[cnt].next=head[u];
        head[u]=cnt;
    }
    void dfs1(int x,int dep){
        d[x]=dep;
        siz[x]=1;
        for(int i=head[x];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa[x])continue;
            fa[v]=x;
            dfs1(v,dep+1);
            siz[x]+=siz[v];
            if(siz[v]>siz[son[x]])son[x]=v;
        }
    }
    void dfs2(int x,int tp){
        top[x]=tp;
        if(son[x])dfs2(son[x],tp);
        for(int i=head[x];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa[x]||v==son[x])continue;
            else dfs2(v,v);
        }
    }
    int query(int a,int b){
        while(top[a]!=top[b]){
            if(d[top[a]]<d[top[b]])swap(a,b);
            a=fa[top[a]];
        }
        return d[a]<d[b]?a:b;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }
        dfs1(1,1);
        dfs2(1,1);
        scanf("%d",&m);
        m--;
        scanf("%d",&a);
        while(m--){
            scanf("%d",&b);
            int tmp=query(a,b);
            ans+=d[a]+d[b]-2*d[tmp];
            a=b;
        }
        printf("%d
    ",ans);
    }

    tarjan版:

    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include <vector>
    using namespace std;
    int n,sum,x,fx,fy,m,l,r,y,a,b,ecnt,tot,head[150010],fa[150010],vis[150010],cnt;
    int s,t,lin[150010][2],d[150010],need,k[150100];
    int ans;
    vector <int> ask[100100];
    struct edge{
        int a,b,next;
    }E[150010];
    void add(int a,int b){
        E[++ecnt].a=a;
        E[ecnt].b=b;
        E[ecnt].next=head[a];
        head[a]=ecnt;
    }
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void Tarjan(int x,int dp){
        d[x]=dp;
        for(int i=head[x];i;i=E[i].next){
            if(!d[E[i].b]){
                Tarjan(E[i].b,dp+1);
                fa[E[i].b]=x;
            }
        }
        int sum=ask[x].size();
          for(int i=0;i<sum;i++){
        int y=ask[x][i];
        if(vis[y]){
         int tt=find(y);
          ans+=d[x]+d[y]-d[tt]*2;
        }
      }
        vis[x]=1;
        return;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
        }
        scanf("%d",&m);
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1;i<=m;i++){
            scanf("%d",&y);
            ask[x].push_back(y),ask[y].push_back(x);
            x=y;
        }    
        d[1]=0;
        Tarjan(1,1);
        printf("%d",ans);
        return 0;
    }

    bzoj1787 紧急集合

    Description

    Input

    Output

    Sample Input

    6 4
    1 2
    2 3
    2 4
    4 5
    5 6
    4 5 6
    6 3 1
    2 4 4
    6 6 6

    Sample Output


    5 2
    2 5
    4 1
    6 0

    HINT

    处理树后,枚举每两个节点求lca再让第三个点连接的情况一共三种。感觉写的有点麻烦了。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int d[500010],siz[500010],head[500010],fa[500010],son[500010],top[500010],cnt,n,m,u,v,a,b,c;
    struct edge{
        int v,next;
    }E[1000010];
    void add(int u,int v){
        E[++cnt].v=v;
        E[cnt].next=head[u];
        head[u]=cnt;
    }
    void dfs(int x,int dep){
        siz[x]=1;
        d[x]=dep;
        for(int i=head[x];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa[x])continue;
            fa[v]=x;
            dfs(v,dep+1);
            siz[x]+=siz[v];
            if(siz[v]>siz[son[x]])son[x]=v;
        } 
    }
    void DFS(int x,int tp){
        top[x]=tp;
        if(son[x])DFS(son[x],tp);
        for(int i=head[x];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa[x]||v==son[x])continue;
            DFS(v,v); 
        }
    }
    int query(int x,int y){
        while(top[x]!=top[y]){
            if(d[top[x]]<d[top[y]])swap(x,y);
            x=fa[top[x]];
        }
        return d[x]<d[y]?x:y;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }
        dfs(1,1);
        DFS(1,1);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&a,&b,&c);
            int l1,l2,l3,t1,t2,t3,l,ans1,ans2,ans3,ans;
            l1=query(a,b);l2=query(b,c);l3=query(a,c);
            t1=query(l1,c);t2=query(l2,a);t3=query(l3,b);
            ans1=d[a]+d[b]-d[l1]*2;
            ans1+=d[l1]+d[c]-d[t1]*2;
            ans2=d[b]+d[c]-d[l2]*2;
            ans2+=d[l2]+d[a]-d[t2]*2;
            ans3=d[a]+d[c]-d[l3]*2;
            ans3+=d[l3]+d[b]-d[t3]*2;
            ans=min(ans1,min(ans2,ans3));
            if(ans==ans1)l=l1;
            else if(ans==ans2)l=l2;
            else l=l3;
            printf("%d %d
    ",l,ans);
        }
    }

    还有一个树剖练习:codevs4633[Mz]树链剖分练习

     

     时间限制: 1 s
     空间限制: 64000 KB
     
     
    题目描述 Description

    给定一棵结点数为n的树,初始点权均为0,有依次q个操作,每次操作有三个参数a,b,c,当a=1时,表示给b号结点到c号结点路径上的所有点(包括b,c,下同)权值都增加1,当a=2时,表示询问b号结点到c号结点路径上的所有点权值之和。

    输入描述 Input Description

    第一行,一个正整数n。

    接下来n-1行,每行一对正整数x,y,表示x号结点和y号结点之间有一条边。

    第n+1行,一个正整数q。

    最后q行,每行一组正整数a,b,c,表示操作的三个参数。b和c可能相等。

    保证数据都是合法的。

    输出描述 Output Description

    若干行,每行一个非负整数表示答案。

    样例输入 Sample Input

    5

    1 2

    2 3

    1 4

    2 5

    5

    1 4 5

    2 1 5

    1 1 3

    2 5 3

    2 4 3

    样例输出 Sample Output

    3

    4

    6

    数据范围及提示 Data Size & Hint

    共有10个测试点,对于第i个测试点,当1<=i<=4时,n=q=10^i,当5<=i<=10时,n=q=10000*i。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define maxn 100010
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    int siz[maxn],tree[maxn],deep[maxn],head[maxn],fa[maxn],son[maxn];
    int sum[maxn<<2],add[maxn<<2];
    int n,ord,top[maxn],ecnt;
    struct edge{
        int u,v,next;
    }E[maxn<<1];
    void added(int u,int v)
    {
        E[++ecnt].u=u;
        E[ecnt].v=v;
        E[ecnt].next=head[u];
        head[u]=ecnt;
    }
    void dfs(int x)
    {
        siz[x]=1;
        for(int i=head[x] ; i ; i=E[i].next )
        {
            int v=E[i].v;
            if(fa[x]==v)continue;
            deep[v]=deep[x]+1;
            fa[v]=x;
            dfs(v);
            siz[x]+=siz[v];
            if(siz[son[x]]<siz[v])son[x]=v;
        }
    }
    void dfs2(int x,int tp)
    {
        tree[x]=++ord;top[x]=tp;
        if(son[x])dfs2(son[x],tp);
        for(int i=head[x] ; i ; i=E[i].next )
        {
            int v=E[i].v;
            if(fa[x]==v||son[x]==v)continue;
            dfs2(v,v);
        }
    }
    void pushup(int o){sum[o]=sum[o<<1]+sum[o<<1|1];}
    void pushdown(int o,int x)
    {
        if(add[o])
        {
            add[o<<1]+=add[o];add[o<<1|1]+=add[o];
            sum[o<<1]+=add[o]*(x-(x>>1));sum[o<<1|1]+=add[o]*(x>>1);
            add[o]=0;
        }
    }
    void update(int o,int l,int r,int ql,int qr)
    {
        if(ql<=l&&r<=qr)
        {
            sum[o]+=r-l+1;
            add[o]++;
            return ;
        }
        int m=(l+r)>>1;
        pushdown(o,r-l+1);
        if(ql<=m)update(lson,ql,qr);
        if(qr>m)update(rson,ql,qr);
        pushup(o);
        return ;
    }
    /*
    int lca(int x,int y)
    {
        while(top[x]!=top[y])
        {
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            x=fa[top[x]];    
        }    
        return deep[x]<deep[y]?x:y; 
    } */
    void do_add(int x,int y)
    {
        int fx=top[x],fy=top[y];
        while(fx!=fy)
        {
            if(deep[fx]<deep[fy]){swap(fx,fy);swap(x,y);}
            update(1,1,n,tree[fx],tree[x]);
            x=fa[fx];fx=top[x];
        }
        if(deep[x]>deep[y])swap(x,y);
        update(1,1,n,tree[x],tree[y]);
        return ;
    }
    int query(int o,int l,int r,int ql,int qr)
    {
        if(ql<=l&&r<=qr)return sum[o];
        pushdown(o,r-l+1);
        int m=(l+r)>>1;
        int ret(0);
        if(ql<=m)ret+=query(lson,ql,qr);
        if(qr>m)ret+=query(rson,ql,qr);
        return ret;
    }
    int que(int x,int y)
    {
        int fx=top[x],fy=top[y];
        int ret(0);
        while(fx!=fy)
        {
            if(deep[fx]<deep[fy]){swap(fx,fy);swap(x,y);}
            ret+=query(1,1,n,tree[fx],tree[x]);
            x=fa[fx];fx=top[x];
        }
        if(deep[x]>deep[y])swap(x,y);
        ret+=query(1,1,n,tree[x],tree[y]);
        return ret;
    }
    int main()
    {
        int a,b,c,q;
        scanf("%d",&n);
        for(int i=1 ; i<n ; ++i )
        {
            scanf("%d%d",&a,&b);
            added(a,b);added(b,a);
        }
        dfs(1);dfs2(1,1);
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(a==1)do_add(b,c);
            else printf("%d
    ",que(b,c));    
        }
        return 0;
    }

    也是个板子题。

    以及一道匈牙利

    codevs2776 寻找代表元

     

     时间限制: 1 s
     空间限制: 256000 KB
     
     
    题目描述 Description

    广州二中苏元实验学校一共有n个社团,分别用1到n编号。
    广州二中苏元实验学校一共有m个人,分别用1到m编号。每个人可以参加一个或多个社团,也可以不参加任何社团。
    每个社团都需要选一个代表。谦哥希望更多的人能够成为代表。

    输入描述 Input Description

    第一行输入两个数n和m。
    以下n行每行若干个数,这些数都是不超过m的正整数。其中第i行的数表示社团i的全部成员。每行用一个0结束。

    输出描述 Output Description

    输出最多的能够成为代表的人数。

    样例输入 Sample Input

    4 4
    1 2 0
    1 2 0
    1 2 0
    1 2 3 4 0

    样例输出 Sample Output

    3

    数据范围及提示 Data Size & Hint

    各个测试点1s

    数据范围
    n,m<=200

    把人和社团分开编号在建边就行。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 203
    int n,m,x,cnt=0;
    int head[2*N],ex[2*N];
    bool vis[2*N];
    struct edge{
        int v,next;
    }E[100010];
    void add(int u,int v){
        E[++cnt].v=v;
        E[cnt].next=head[u];
        head[u]=cnt;
    }
    bool find(int x){
        vis[x]=1;
        for(int i=head[x];i;i=E[i].next){
            int v=E[i].v;
            if(!vis[v]){
                vis[v]=1;
                if(!ex[v]||find(ex[v])){
                    ex[v]=x;
                    ex[x]=v;
                    return true;
                }
            }
        }
        return false;
    }
    int match(){
        int ans=0;
        for(int i=1;i<=n;i++){
            memset(vis,0,sizeof(vis));
            if(find(i))ans++;
        }
        return ans;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            while(1){
                scanf("%d",&x);
                if(x){
                    add(i,x+200);//分开编号
                    add(x+200,i);
                }
                else break;
            }
        }
        printf("%d
    ",match());
        return 0;
    }

    复习模板有希望啊~

  • 相关阅读:
    一个故事告诉你,数据分析如何给企业带来价值
    【CS231n】斯坦福大学李飞飞视觉识别课程笔记(九):最优化笔记(上)
    区块链P2P网络详细讲解
    互联网协议入门
    BitTorrent DHT 协议中文
    基于侧链的P2P网络设计
    【转】P2P-BT对端管理协议
    P2P网络与BitTorrent技术简介
    【COCOS2DX-LUA 脚本开发之四】使用tolua++编译pk创建自定义类
    爱创课堂每日一题第五十四天- 列举IE 与其他浏览器不一样的特性?
  • 原文地址:https://www.cnblogs.com/Requiescat/p/7725777.html
Copyright © 2011-2022 走看看