题目大意:给定一棵树,每个点有一个0/1的权,一次操作可以使得x和x的直接邻居点权^1,初始均为0,求最少多少次操作可以全部变成1
题解:树形dp可以
O(n)
解决
但这不重要…………………………
考虑对每个点列出一个xor方程,这样就可以高斯消元了……
但是有可能有自由元……暴力枚举代入计算
我的收获:gauss暴力!
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <bitset>
#include <cstring>
using namespace std;
const int M=105;
int n,tot;
int c[M],ans[M];
bitset<210> a[M];
void Gauss(){
for(int i=1;i<=n;i++){
int r=i;
while(!a[r][i]&&r<=n) r++;
if(r==n+1){c[++tot]=i;continue ;}
swap(a[i],a[r]);
for(int k=1;k<=n;k++) if(k!=i&&a[k][i]) a[k]^=a[i];
}
}
int calc(int x)
{
int cnt=0;
for(int i=1;i<=n;i++) ans[i]=a[i][n+1];
for(int i=1;i<=tot;i++)
if(x&(1<<(i-1))){
cnt++;
for(int j=1;j<=n;j++)
if(a[j][c[i]]) ans[j]=!ans[j];
}
for(int i=1;i<=n;i++) cnt+=ans[i];
return cnt;
}
void work()
{
tot=0;Gauss();
int ret=n;
for(int i=0;i<(1<<tot);i++) ret=min(ret,calc(i));
printf("%d\n",ret);
}
void init()
{
for(int i=1;i<=n;i++) a[i].reset(),a[i][i]=1,a[i][n+1]=1;
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
a[x][y]=1;a[y][x]=1;
}
}
int main()
{
while(scanf("%d",&n)!=EOF&&n) init(),work();
return 0;
}
1355

被折叠的 条评论
为什么被折叠?



