1. 程式人生 > >2017.10.24

2017.10.24

printf -1 bsp tmp next als 參數 () 解釋

今天主要進行了一些模板的復習。主要是樹剖lca方面。感覺雖然是學過的東西,不寫的話還是很容易忘的。

商務旅行


時間限制: 1 s 空間限制: 128000 KB 題目描述 Description

某首都城市的商人要經常到各城鎮去做生意,他們按自己的路線去做,目的是為了更好的節約時間。

假設有N個城鎮,首都編號為1,商人從首都出發,其他各城鎮之間都有道路連接,任意兩個城鎮之間如果有直連道路,在他們之間行駛需要花費單位時間。該國公路網絡發達,從首都出發能到達任意一個城鎮,並且公路網絡不會存在環。

你的任務是幫助該商人計算一下他的最短旅行時間。

輸入描述 Input Description

輸入文件中的第一行有一個整數N,1<=n<=30 000,為城鎮的數目。下面N-1行,每行由兩個整數ab (1<=a, b<=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;
}

復習模板有希望啊~

2017.10.24