【思维题 状压dp】APC001F - XOR Tree

可能算是道中规中矩的套路题吧……

Time limit : 2sec / Memory limit : 256MB

Problem Statement

You are given a tree with N vertices. The vertices are numbered 0 through N−1, and the edges are numbered 1 through N−1. Edge i connects Vertex xi and yi, and has a value ai. You can perform the following operation any number of times:

  • Choose a simple path and a non-negative integer x, then for each edge e that belongs to the path, change ae by executing ae←ae⊕x (⊕ denotes XOR).

Your objective is to have ae=0 for all edges e. Find the minimum number of operations required to achieve it.

Constraints

  • 2≤N≤105
  • 0≤xi,yi≤N−1
  • 0≤ai≤15
  • The given graph is a tree.
  • All input values are integers.

题目大意

给定一个 $n$ 个节点的树,节点的标号为 $0\sim n-1$,边的标号为 $1\sim n-1$。每条边 $i$ 连接节点 $x_i$ 和 $y_i$,并且有一个权值 $a_i$。你可以进行如下的操作若干次。

  • 选择一条简单路径以及一个非负整数 $x$,然后对于每条属于这条路径的边,将它的权值异或上 $x$。

你的目标是让所有边的权值变成 $0$,同时,最小化操作的次数。


题目分析

我的初步想法是考虑对路径权值按位拆分。可能是受以前做过的一道序列一维问题的影响吧,就一直朝着这个思路想下去了……这个思路的关键在于没法处理不同位的路径的合并。

考虑设计一个守恒的部分量:将每个点记点权为相连所有路径边权的异或和。这么处理的好处在于对路径(u,v)进行一次操作之后,全图只有u,v的点权改变。对于点权相同的点对,最优操作当然是直接将它们消去;于是最后剩下的点权最多只有16种。注意到点权0是没有影响的,所以处理完点权之后,再对剩下的15种点权做一遍状压dp就可以了。

//CXR的快读板子怎么这么快

 

 1 #include<bits/stdc++.h>
 2 #define Tp template<typename Ty>
 3 #define Ts template<typename Ty,typename... Ar>
 4 #define Reg register
 5 #define RI Reg int
 6 #define Con const
 7 #define CI Con int&
 8 #define I inline
 9 #define W while
10 #define N 100000
11 #define P 15
12 #define INF 1e9
13 #define Gmin(x,y) (x>(y)&&(x=(y)))
14 const int maxn = 100035;
15 const int maxs = 1<<17;
16 
17 int n,cnt,a[maxn],f[maxs],sta,ans;
18 
19 class Class_FIO
20 {
21     private:
22         #define FS 100000
23         #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
24         #define pc(c) (putchar(c))
25         #define tn(x) (x<<3)+(x<<1)
26         #define D isdigit(c=tc())
27         int T;char c,*A,*B,FI[FS],S[FS];
28     public:
29         I Class_FIO() {A=B=FI;}
30         Tp I void read(Ty& x) {x=0;W(!D);W(x=tn(x)+(c&15),D);}
31         Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
32         Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
33 }F;
34 int dp(int x)
35 {
36     if (!x) return 0;
37     if (f[x]!=-1) return f[x];
38     int ret = 1e9;
39     for (int i=1; i<16; i++)
40         if (x&(1<<i)) for (int j=1; j<16; j++)
41             if ((x&(1<<j))&&i!=j)
42                 ret = std::min(ret, dp(x^(1<<i)^(1<<j)^(1<<(i^j)))+1+((x&(1<<(i^j)))?1:0));
43     f[x] = ret;
44     return ret;
45 }
46 int main()
47 {
48     F.read(n);
49     memset(f, -1, sizeof f);
50     for (int i=1; i<n; i++)
51     {
52         int x,y,z;
53         F.read(x), ++x, F.read(y), ++y, F.read(z);
54         a[x] ^= z, a[y] ^= z;
55     }
56     for (int i=1; i<=n; i++)
57         if (a[i]&&((sta>>a[i])&1)) sta ^= 1<<a[i], ++ans;
58         else if (a[i]) sta ^= 1<<a[i];
59     printf("%d\n",ans+dp(sta));
60     return 0;
61 }

 

 

END

转载于:https://www.cnblogs.com/antiquality/p/10392368.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值