BZOJ 2466 [中山市选2009]树(高斯消元)

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2466

【题目大意】

  给定一棵树,每个节点有一盏指示灯和一个按钮。如果节点的按扭被按了,
  那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭
  并且该节点的直接邻居也发生同样的变化。开始的时候,所有的指示灯都是熄灭的。
  请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。

【题解】

  高斯消元枚举自由变元回代。

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
namespace Gauss{
    const int N=110,MOD=2,INF=1e9;
    int a[N][N],ans[N];
    bool isFreeX[N];
    int inv(int a,int m){return(a==1?1:inv(m%a,m)*(m-m/a)%m);}
    int getAns(int n,int m,int r){
        int res=0;
        for(int i=r-1;~i;i--){
            for(int j=0;j<m;j++){
                if(!a[i][j])continue;
                ans[j]=a[i][m];
                for(int k=j+1;k<m;k++){
                    ans[j]-=a[i][k]*ans[k];
                    ans[j]%=MOD;
                    if(ans[j]<0)ans[j]+=MOD;
                }
                ans[j]=ans[j]*inv(a[i][j],MOD)%MOD;
                break;
            }
        }
        for(int i=0;i<m;i++)res+=ans[i];
        return res;
    }
    int gauss(int n,int m){
        for(int i=0;i<m;i++)isFreeX[i]=0;
        int r=0,c=0;
        for(;r<n&&c<m;r++,c++){
            int maxR=r;
            for(int i=r+1;i<n;i++)if(abs(a[i][c])>abs(a[maxR][c]))maxR=i;
            if(maxR!=r)swap(a[maxR],a[r]);
            if(!a[r][c]){r--;isFreeX[c]=1;continue;}
            for(int i=r+1;i<n;i++){
                if(a[i][c]){
                    int delta=a[i][c]*inv(a[r][c],MOD);
                    for(int j=c;j<=m;j++){
                        a[i][j]-=delta*a[r][j];
                        a[i][j]%=MOD;
                        if(a[i][j]<0)a[i][j]+=MOD;
                    }
                }
            }
        }
        for(int i=r;i<n;i++)if(a[i][m])return -1;
        return r;
    }
    // 模2枚举自由变元
    int getMinAns(int n,int m,int r){
        int res=INF,freeX=m-r;
        for(int s=0;s<1<<freeX;s++){
            if(__builtin_popcount(s)>=res)continue;
            int cnt=0;
            for(int j=0;j<m;j++){
                if(isFreeX[j]){
                    ans[j]=s>>cnt&1;
                    ++cnt;
                }
            }res=min(res,getAns(n,m,r));
        }return res;
    }
}
int n,x,y;
int main(){
    while(~scanf("%d",&n),n){
        using namespace Gauss;
        memset(a,0,sizeof(a));
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            a[x-1][y-1]=1;
            a[y-1][x-1]=1;
        }
        for(int i=0;i<n;i++)a[i][i]=a[i][n]=1;
        int r=gauss(n,n);
        printf("%d\n",getMinAns(n,n,r));
    }return 0;
}
时间: 2024-11-29 06:25:20

BZOJ 2466 [中山市选2009]树(高斯消元)的相关文章

BZOJ 2466: [中山市选2009]树( 高斯消元 )

高斯消元解异或方程组...然后对自由元进行暴搜.树形dp应该也是可以的... -------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<bitset> using namespace std; const int ma

BZOJ 2466 中山市选2009 树 高斯消元+暴力

题目大意:树上拉灯游戏 高斯消元解异或方程组,对于全部的自由元暴力2^n枚举状态,代入计算 这做法真是一点也不优雅... #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 110 using namespace std; int n,m; int f[M][M],is_free[M],tot; int ans[M],cnt; void

BZOJ 2466: [中山市选2009]树

Sol 树形DP. 听说有非常神奇的高斯消元的做法...orz... 然而我只会 \(O(n)\) 的树形DP. 首先一个点的状态只于他的父节点和子树有关,跟他 子树的子树 和 父亲的父亲 都没有任何关系. 这样就可以DP了. \(f[i][0/1][0/1]\) 表示\(i\)节点,点了 \(0/1\) 次,状态是 \(0/1\) (暗或亮). 然后就瞎转移就可以了,具体看代码吧. Code /**************************************************

2466: [中山市选2009]树

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1416  Solved: 614[Submit][Status][Discuss] Description 图论中的树为一个无环的无向图.给定一棵树,每个节点有一盏指示灯和一个按钮.如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的).并且该节点的直接邻居也发生同样的变化. 开始的时候,所有的指示灯都是熄灭的.请编程计算最少要按多少次按钮,才

bzoj2466: [中山市选2009]树

同上一题.(应该可以树形dp,然而我不会... #include<cstdio> #include<cstring> #include<iostream> #include<bitset> #include<algorithm> using namespace std; #define REP(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #

【BZOJ2466】[中山市选2009]树 树形DP

[BZOJ2466][中山市选2009]树 Description 图论中的树为一个无环的无向图.给定一棵树,每个节点有一盏指示灯和一个按钮.如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的).并且该节点的直接邻居也发生同样的变化. 开始的时候,所有的指示灯都是熄灭的.请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态. Input 输入文件有多组数据. 输入第一行包含一个整数n,表示树的节点数目.每个节点的编号从1到n.

【bzoj4184】shallot 线段树+高斯消元动态维护线性基

题目描述 小苗去市场上买了一捆小葱苗,她突然一时兴起,于是她在每颗小葱苗上写上一个数字,然后把小葱叫过来玩游戏. 每个时刻她会给小葱一颗小葱苗或者是从小葱手里拿走一颗小葱苗,并且 让小葱从自己手中的小葱苗里选出一些小葱苗使得选出的小葱苗上的数字的异或和最大. 这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为Oi选手的你,你能帮帮他吗? 你只需要输出最大的异或和即可,若小葱手中没有小葱苗则输出0. 输入 第一行一个正整数n表示总时间:第二行n个整数a1,a2...an,

【BZOJ2466】【中山市选2009】树 高斯消元解异或方程组

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44356273"); } 题解: 参照此题解,也是我写的,俩题一样. [POJ1681]Painter's Problem 高斯消元,求最小∑系数的异或方程组 代码: #include <cmath> #include &

[BZOJ 3143][Hnoi2013]游走(高斯消元+期望)

Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数.当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和. 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小. Solution 对于点u(u≠1):到达u的概率 f[u]=∑f[v]/d[v] (Edges(u,v)) 而f[1]=∑f[v]/d[v]+1