欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C. Dynamic Graph Matching

程序员文章站 2024-03-23 23:12:04
...

C. Dynamic Graph Matching

题意

给定一个 n 个点的无向图,m 次加边或者删边操作。
在每次操作后统计有多少个匹配包含 k = 1, 2, …,n/2 条边。
匹配的定义: 边的集合,没有共同的顶点

分析

先把匹配的概念转换,n个边的匹配,就是n条边没有共同的顶点,那就是2*n个点,没有共同的边
那么问题就转化成点,1,2,3....n/2 条边,其实就是2,4,6,8,..n 个点的集合,集合中的点没有共同的边

首先这是一个dp问题
阶段: 每一个加边或者减边的操作
状态: S 集合 (S 集合包含哪些点)
状态转移方程

F[0]=1

F[S]=uvS{F[S]+F[Suv]F[S]F[Suv]

加边比较好理解就是加F[S] 由两部分组成,一部分是之前S集合匹配的个数F[S],一部分是之前不含有(u,v) 顶点,现在加入(u,v) 顶点形成S集合 F[S-u-v]
减边的时候可以这样理解,假设上一次刚好加的就是这条边,那么使用的是+操作,这回把上一次加上的减去这样理解

参考代码

附上和标程几乎一模一样的代码

const int N = (1<<10);
int T,n,m,u,v,S;
int cnt[N];
int F[N];
int ans[15];
// 状态转移方程
// 加边的时候                     F[S]  = F[S]+F[S-u-v]
// 减去这条边的时候把原来加的减去 F[S]  = F[S]-F[S-v-v]
int main(void)
{
   for(int i = 0;i < N; ++i) cnt[i] = __builtin_popcount(i);
   scanf("%d",&T);
   while(T--){
    scanf("%d %d",&n,&m);
    int all = 1<<n;
    for(int i = 0;i < all; ++i) F[i] = 0;
    F[0] = 1;
    while(m--){
        char c[10];
        scanf("%s %d%d",c,&u,&v);
    //  cout<<c<<u<<v;
        u--,v--;
        S = (1<<u)|(1<<v);
        if(c[0] == '+'){
                for(int i = 0;i < all; ++i){
                    if(!(S&i)) F[i^S] =( F[i^S] + F[i])%mod;
                  }
           }
        else {
               for(int i = 0;i < all; ++i){
                   if(!(S&i)) F[i^S] = (F[i^S] - F[i])%mod;
               }
        }
        for(int i = 0;i <= n; ++i)
           ans[i] = 0;
        for(int i = 0;i < all; ++i)
           ans[cnt[i]] = (ans[cnt[i]]+F[i])%mod;
        for(int i = 2;i <= n; i += 2){
           printf("%d%c",(ans[i]%mod+mod)%mod," \n"[i==n]);
          }
       }

   }

   return 0;
}


相关标签: 状态压缩