[ural 1129] 欧拉回路

题意:

幼儿园里有许多房间,之间是走廊和门。一个装修计划即将执行。 这些门允许涂上明快的颜色:绿色和黄色。园长希望满足以下条件:任意一扇门的各个面必须不同颜色。每一个房间绿色门的数量,与黄色门的数量之差最多为1。给定园长的计划,请提出你的安排。

分析:

首先,对于无向图,度为奇数的点必然有偶数个。

下面证明一下:

如果加入边(i,j),有下面几种情况:

1、两个点原来的度一奇一偶,那么加入边以后,度为奇数的点的数量没变

2、两个点原来的度都是奇数,加入边后,度为奇数的点的数量-2

3、两个点原来的度都为偶数,加入边后,度为奇数的点的数量+2

由于只有3个种情况-2,+0,+2,所以,度为奇数的点的数量必为偶数个。

于是命题得证。

然后我们对度为奇数的同一连通分量的点连边,这样就使图不存在度为奇数的点了。

于是我们利用欧拉回路可以使颜色数量相等。

最后我们把加的边删掉。

由于每个点都只会加一条边,所以G和Y的差距不会超过1。

code:

#include #include struct gtp{int color,next;}c[100000];
int g[101][101],n,ls[101][101],ct,du[101],L[100000],Lt,a[101][101],e=0,v[101];
bool flag[101];

void init(){
    memset(g,0,sizeof(g));
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        int num,tmp;
        scanf("%d",&num);
        for (int j=1;j<=num;j++){
            scanf("%d",&tmp);
            g[i][tmp]++;
        }   
    }   
}   

void dfs(int k){
    for (int i=1;i<=n;i++)
      if (a[i][k]>0){
          a[i][k]--;
          a[k][i]--;
          dfs(i);
      }   
    L[++Lt]=k;
}   

void filldfs(int k){
    for (int i=1;i<=n;i++)
      if (g[k][i]>0 && v[i]==0){
          v[i]=v[k];
          filldfs(i);
      }           
}   

void fill_color(){
    memset(v,0,sizeof(v));
    for (int i=1;i<=n;i++)
      if (v[i]==0) {
          v[i]=++ct;
          filldfs(i);
      }   
}   

void addcolor(int x,int y){
    e++;
    c[e].color=0;
    c[e].next=ls[x][y];
    ls[x][y]=e;
    e++;
    c[e].color=1;
    c[e].next=ls[y][x];
    ls[y][x]=e;
}   

void eur(){
    memset(du,0,sizeof(du));
    memcpy(a,g,sizeof(g));
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
        if (g[i][j]>0) du[j]+=g[i][j];
    for (int i=1;i<=n;i++)
      if (du[i]%2==1)
        for (int j=i+1;j<=n;j++)
          if (du[j]%2==1 && v[i]==v[j]){
              du[i]++;
              du[j]++;
              a[i][j]++;
              a[j][i]++;
              break;
          }
    memset(flag,false,sizeof(flag));
    for (int i=1;i<=n;i++)
      if (!flag[v[i]]){
        flag[v[i]]=true;
        Lt=0;
        dfs(i);
        for (int j=1;j          addcolor(L[j],L[j+1]);
    }   
}   

void print(){
    for (int i=1;i<=n;i++){
      for (int j=1;j<=n;j++){
          int t=ls[i][j];
          for (int num=g[i][j];num>=1;num--){
              if (c[t].color==0) printf("G ");
                            else printf("Y ");
              t=c[t].next;
          }   
      }       
      printf("n");
}   
}   

int main(){
    init();
    fill_color();
    eur();
    print();
}   

[POJ 2762] 弱连通分量

题意:

给定N个点,E条边的有向图,问图中是否任意两个点u,v都有

(u能到达v)or(v能到达u)

分析:

首先环内的肯定是满足的,所以先缩点。

然后剩下一棵树。

那么从根节点(即缩点后没有入度的点)出发,假设一路走下去是一条链的话(即树没有分叉),而且所有的点都能走到,那么就满足性质。

证明:

1、如果有分叉的话,分叉两边的点不能到达对方。

2、入度为0的点不可能没有,因为已经缩点,不存在环了。

3、入度为0的如果>1,那么两个入度为0的点不能到达对方。

所以,结论就是,只有所有收缩后的点成一长链,才能满足性质。

插曲:

Tarjan居然写错了= =,真想抽自己一下

code:

#include

[SGU 242] 网络流

题意:

有N个学生,可以被分配去M个学校,给出哪些学生能去哪些学校,

求一个解:每间学校都分到>=2个学生的方案。

分析:

cai0715用上下界做。。。其实不用这么麻烦= =

连边(s,i,1) i为学生

连边(i,j,1) j为学校

连边(j,T,2)

然后最大流即可。

插曲:

PE到2B,原来SGU的PE有可能是Runtime ERROR。原来我数组没开够

code:

#include #include #define N 100000
struct gtp{int x,y,c,next,op;}g[N];
int e,n,m,ls[N],flow,cur[N],fa[N],d[N],num[N],list[N][3];

void add(int x,int y,int c){
    e++;
    g[e].x=x;
    g[e].y=y;
    g[e].c=c;
    g[e].next=ls[x];
    ls[x]=e;
    g[e].op=e+1;
   
    e++;
    g[e].x=y;
    g[e].y=x;
    g[e].c=0;
    g[e].next=ls[y];
    ls[y]=e;
    g[e].op=e-1;
}   

void init(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        add(0,i,1);
        int num,x;
        scanf("%d",&num);
        for (int j=1;j<=num;j++){
            scanf("%d",&x);
            add(i,n+x,1);
        }
    }
    for (int i=1;i<=m;i++) add(i+n,n+m+1,2);
}   

void relabel(int k){
    int min=n+m+1;
    cur[k]=ls[k];
    for (int t=ls[k];t!=0;t=g[t].next)
        if (g[t].c>0 && d[g[t].y]    d[k]=min+1;
}   

void change(){
    int nf=2;
    for (int i=n+m+1;i!=0;i=g[fa[i]].x)
      if (g[fa[i]].c    flow+=nf;
    for (int i=n+m+1;i!=0;i=g[fa[i]].x){
        g[fa[i]].c-=nf;
        g[g[fa[i]].op].c+=nf;
    }   
}   

void sap(){
    num[0]=n+m+2;
    for (int i=0;i<=n+m+1;i++) cur[i]=ls[i];
    int i=0;
    while (d[0]        while (cur[i]!=0){
            if (g[cur[i]].c>0 && d[g[cur[i]].y]+1==d[i]) break;
            cur[i]=g[cur[i]].next;
        }
        if (cur[i]==0){
            num[d[i]]--;
            if (num[d[i]]==0) break;
            relabel(i);
            num[d[i]]++;
            if (i!=0) i=g[fa[i]].x;
        }   
        else{
            fa[g[cur[i]].y]=cur[i];
            i=g[cur[i]].y;
            if (i==n+m+1){
                change();
                i=0;
            }   
        }   
    }
}   

void addlist(int key,int pos){
    list[pos][++list[pos][0]]=key;
}   

void print(){
    if (flow        printf("NOn");
        return;
    }   
    printf("YESn");
    for (int i=1;i      if (g[i].x==0 && g[i].c==0){
          int t=ls[g[i].y];
          while (t!=0){
              if (g[t].y>=n+1 && g[t].y<=n+m && g[t].c==0){
                  addlist(g[i].y,g[t].y-n);
                  break;
              }   
              t=g[t].next;
          }   
      }   
    for (int i=1;i<=m;i++)
        printf("2 %d %dn",list[i][1],list[i][2]);
}   

int main(){
    init();
    sap();
    print();
    return 0;
}   

[POJ 2763] 树状数组 LCA RMQ

题意:

给定一棵无根树(边无向),你一开始在s点上。

每次有两种操作。

1、0 x

表示走到x点上

2、1 i w

将第i条边的权值变为w

分析:

首先如果定义一个根,且d[i]为i点到根的距离。

那么必有dis(i,j)=d[i]+d[j]-2*d[lca(i,j)]

先按dfs顺序把点放进一个队列,这样如果以某点为根的子树的结点就会连续排列

这样如果把某一条边改变以后,就相当如把以深度较大的那个点的为根的子树的所有d值都变为那个值。

那么这样的话就可以用线段树或者树状数组实现了。

插曲:

WA了很多遍,一开始work里很多东西写错,后来发现LCA写错。

cai0715-problem-solutions数据结构部分正式结束。

code:

#include #include #define N 222222
struct gtp{int x,y,w,next;}g[N];
int n,q,cur,ls[N],e,dep[N],d[N];
int Ql[N],Qt=0,pl[N],pr[N],El[N],Et=0,R[N],log2[N];

struct rmqtype{
    int w[20][N],who[20][N];
    void build(){
        int maxk=log2[Et];
        for (int i=1;i<=Et;i++) {
            w[0][i]=dep[El[i]];
            who[0][i]=El[i];
        }
        for (int k=1;k<=maxk;k++){
            int t=1<            for (int i=1;i+(1<              if (w[k-1][i]                  w[k][i]=w[k-1][i];
                  who[k][i]=who[k-1][i];
              }
              else{
                  w[k][i]=w[k-1][i+t];
                  who[k][i]=who[k-1][i+t];
              }
        }
    }
    int lca(int l,int r){
        int t;
        if (r        t=log2[r-l+1];
        if (w[t][l]        return who[t][r-(1<    }
}rmq;

struct Tree_Array_Type{
    int a[N];
    void clear(){memset(a,0,sizeof(a));}
    void add(int pos,int x){
        for (int i=pos;i<=n;i+=(i & -i)) a[i]+=x;
    }
    int sum(int pos){
        int t=0;
        for (int i=pos;i>=1;i-=(i & -i)) t+=a[i];
        return t;
    }
}Tarr;

void addedge(int x,int y,int w){
    e++;
    g[e].x=x; g[e].y=y; g[e].w=w; g[e].next=ls[x]; ls[x]=e;
}

void init(){
    memset(ls,0,sizeof(ls));
    memset(dep,0,sizeof(dep));
    int pre=-1;
    for (int i=1;i<=N;i++)
      if ((1<<(pre+1))==i) log2[i]=++pre;
                      else log2[i]=pre;
    scanf("%d%d%d",&n,&q,&cur);
    for (int i=1;i<=n-1;i++){
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        addedge(x,y,w);
        addedge(y,x,w);
    }
}

void predfs(int p,int nowdep,int distance){
    El[++Et]=p;
    R[p]=Et;
    Ql[++Qt]=p;
    dep[p]=nowdep;
    pl[p]=Qt;
    d[p]=distance;
    for (int t=ls[p];t>0;t=g[t].next)
      if (dep[g[t].y]==0) {
          predfs(g[t].y,nowdep+1,distance+g[t].w);
          El[++Et]=p;
      }
    pr[p]=Qt;
}

void Build(){
    Tarr.clear();
    for (int i=1;i<=n;i++) {
        Tarr.add(pl[i],d[i]);
        Tarr.add(pl[i]+1,-d[i]);
    }
    rmq.build();
}

void work(){
    int op,x,y;
    for (int k=1;k<=q;k++){
        scanf("%d",&op);
        if (op==0){
           scanf("%d",&x);
           int fa=rmq.lca(R[cur],R[x]);
           printf("%dn",Tarr.sum(pl[cur])+Tarr.sum(pl[x])-2*Tarr.sum(pl[fa]));
           cur=x;
        }
        else{
            scanf("%d%d",&x,&y);
            int p=g[x*2].x;
            if (dep[g[x*2].x]            int change=y-g[x*2].w;
            Tarr.add(pl[p],change);
            Tarr.add(pr[p]+1,-change);
            g[x*2-1].w=y;
            g[x*2].w=y;
        }
    }
}

int main(){
    init();
    predfs(1,1,0);
    rmq.build();
    Build();
    work();
    return 0;
}

SGU155 笛卡尔树

题意:

给定N个二元组(Ki,Ai)

让你建立一棵笛卡尔树。

这个笛卡尔树实际上就是一个Treap.

k为二叉排序树的关键字,a为堆的关键字,堆是一个小根堆。

分析:

直接Treap会TLE,因为他给定了关键值,会退化成O(n^2)

然后我们可以根据Ki把结点从小到大排序,

然后每次都是最右边插入新的结点,

然后维护只需要左旋(他们叫右旋= =我觉得向左比较形象)

只有左旋的话我们可以注意到两个性质:

1、那么就是旋转以后,我这个结点不会增加新的儿子。

2、旋转以后,我必然是下一次插入的点的父结点。

别问我问什么。。。手画一下。。。证明这东西不是我这个级别做的事

然后我们可以注意到,从根开始连续向右到底这条路径可以看成一个

每次左旋,就是一次出栈操作。

每次加结点,就是一次入栈操作。

于是,加结点和左旋操作数都<=n

所以,证明了这个算法的复杂度是:O(N)!

  

HINT

1A。

code:

#include #include #define N 60000
using namespace std;
struct re{int k,a,wz;}d[N];
struct trtype{int l,r,fa,zz;}tr[N];
int n,tot,mr,locate[N];

void left(int t){
    int p=tr[t].fa;
    tr[p].r=tr[t].l;
    tr[tr[t].l].fa=p;
    tr[t].l=p;
    tr[0].l=0; tr[0].r=0; tr[0].fa=0; tr[0].zz=0;
    if (tr[tr[p].fa].l==p) tr[tr[p].fa].l=t;
    if (tr[tr[p].fa].r==p) tr[tr[p].fa].r=t;
    tr[t].fa=tr[p].fa;
    tr[p].fa=t;
}   

void work(){
    tot=1; mr=1; tr[1].l=0; tr[1].r=0; tr[1].fa=0; tr[1].zz=1;
    for (int i=2;i<=n;i++){
        tot++;
        tr[tot].l=0; tr[tot].r=0; tr[tot].zz=i; tr[tot].fa=mr; tr[mr].r=tot;
        mr=tot;
        int now=tot;
        while (tr[now].fa!=0 && d[tr[tr[now].fa].zz].a>d[i].a)
          left(now);
    }   
}   

inline int wz(int x){
    return d[tr[x].zz].wz;
}   

void print(){
    printf("YESn");
    for (int i=1;i<=tot;i++) locate[d[tr[i].zz].wz]=i;
    for (int i=1;i<=n;i++){
        int t=locate[i];
        printf("%d %d %dn",wz(tr[t].fa),wz(tr[t].l),wz(tr[t].r));
    }   
}   

bool cmp(re x,re y){
    if (x.k    return false;
}   

int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%d%d",&d[i].k,&d[i].a);
        d[i].wz=i;
    }
    sort(&d[1],&d[n+1],cmp);
    work();
    print();
    return 0;
}