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\n",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\n",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\n",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\n",match());
    return 0;
}

复习模板有希望啊~

posted @ 2017-10-24 20:56  Requiescat  阅读(130)  评论(0编辑  收藏  举报