• The 2019 ACM-ICPC China Shannxi Provincial Programming Contest


    A - Tasks

    dp或者贪心

    #define x first
    #define y second
    #define pb push_back
    #define ls rt << 1
    #define rs rt << 1 | 1
    typedef long long ll ;
    const double esp = 1e-6 , pi = acos(-1) ;
    typedef pair<int , int> PII ;
    const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
    vector<int> v ;
    int n , m , a[N] , dp[N] ;
    int work()
    {
      cin >> n >> m ;
      for(int i = 1; i <= n ;i ++ ) cin >> a[i] ;
      sort(a + 1 , a + n + 1) ;
      int ans = 0 ;
      for(int i = 1; i <= n ;i ++ ) {
        for(int j = m ;j >= a[i] ;j -- ) 
           dp[j] = max(dp[j] , dp[j - a[i]] + 1) , ans = max(ans , dp[j]);
      }
      cout << ans << "
    " ;
      return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
    
      work() ;
      return 0 ;
    }
    /*
    
    */
    

    B - Product

    杜教筛

    [prod_{i = 1}^{n}prod_{j = 1}^{n}prod_{k = 1}^{n} m^{gcd(i , j) [k |gcd(i , j)]} \% p ]

    欧拉降幂

    [= m ^ {sum_{i = 1} ^{n}sum_{j = 1} ^{n}sum_{k = 1} ^{n}gcd(i , j) [k |gcd(i , j)] \% (p - 1)} \% p ]

    只要求指数,然后快速幂即可

    [sum_{i = 1} ^{n}sum_{j = 1} ^{n}sum_{k = 1} ^{n}gcd(i , j) [k |gcd(i , j)] \% (p - 1) ]

    看式子意思是只要k是gcd(i , j)的因子,gcd(i , j) 就有一次贡献,枚举gcd(i , j) , 设d[p] 是 p因子的个数

    [sum_{p = 1}^{n} p * d[p] sum_{i = 1}^{n} sum_{j = 1}^{n}[gcd(i , j) == p] \=sum_{p = 1}^{n} p * d[p] sum_{i = 1}^{frac{n}{p}} sum_{j = 1}^{frac{n}{p}}[gcd(i , j) == 1] ]

    [F(n) =sum_{i = 1}^{frac{n}{p}} sum_{j = 1}^{frac{n}{p}}[gcd(i , j) == 1] \=>2sum_{i = 1}^{n}phi(i) - 1 \ S(n) = sum_{i = 1}^{n}phi(i) \ F(n) = 2 * S(n) - 1 \ 原式=sum_{p = 1}^{n} p * d[p] * F(frac{n}{p}) ]

    求S(n) , 经典杜教筛

    [S(n) = sum_{i = 1}^{n}f(i) \ f(i) = phi(i) ]

    迪利克雷卷积前缀和,将f乘一个积性函数g

    [S(n) = sum_{i = 1}^{n} g * f(i) \= sum_{i = 1}^{n} sum_{d|i} g(d) * f(frac{i}{d})\= sum_{d = 1}^{n}g(d) * sum_{d|i}f(frac{i}{d})\= sum_{d=1}^{n}g(d) * sum_{i = 1}^{frac{n}{d}}f(i)\= sum_{d=1}^{n}g(d) * S(frac{n}{d}) ]

    进行一下充斥

    [g(1) S(n) = sum_{d=1}^{n}g(d) * S(frac{n}{d}) - sum_{d=2}^{n}g(d) * S(frac{n}{d})\= g(1)S(n) =sum_{i = 1}^{n} g * f(i)- sum_{d=2}^{n}g(d) * S(frac{n}{d})\= g(1)S(n) =sum_{i = 1}^{n} g * f(i)- sum_{d=2}^{n}g(d) * S(frac{n}{d})\= sum_{i = 1}^{n} sum_{d|i} g(d) * f(frac{i}{d})-sum_{d=2}^{n}g(d) * S(frac{n}{d}) ]

    [sum_{d|i} g(d) * f(frac{i}{d}) = sum_{d|i} g(d) * phi(frac{i}{d}) \ sum_{d|n}phi(frac{n}{d}) = n\ ]

    取g函数为积性函数I(i) = 1 , 恒为1

    [sum_{d|i} g(d) * f(frac{i}{d}) \= sum_{d|i} I(d) * phi(frac{i}{d}) \= i\ g(1) S(n) =sum_{i = 1}^{n} sum_{d|i} g(d) * f(frac{i}{d})-sum_{d=2}^{n}g(d) * S(frac{n}{d})\ S(n) = sum_{i = 1}^{n} i -sum_{d=2}^{n}g(d) * S(frac{n}{d})\ S(n)=frac{n * (n + 1)}{2} - sum_{d=2}^{n}S(frac{n}{d}) ]

    [原式=sum_{p = 1}^{n} p * d[p] * F(frac{n}{p}) \F(n) = 2 * S(n) - 1 ]

    最后如何求解

    [sum_{p = 1}^{n} p * d[p] , d[p] 是p的因子个数 \ sum_{p = 1}^{n}sum_{d|p}p \=sum_{d=1}^{n}d sum_{p=1}^{frac{n}{d}}p\=sum_{d=1}^{n}d*frac{(1+frac{n}{d})*frac{n}{d}}{2} ]

    [原式=sum_{p = 1}^n p*frac{(1+frac{n}{p})*frac{n}{p}}{2} * (2S(frac{n}{p}) - 1) \S(n) = sum_{i = 1} ^ n phi(i) ]

    直接对结果进行分块处理就好了。

    #define x first
    #define y second
    #define pb push_back
    #define ls rt << 1
    #define rs rt << 1 | 1
    typedef long long ll ;
    const double esp = 1e-6 , pi = acos(-1) ;
    typedef pair<int , int> PII ;
    const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
    ll n , m , p ;
    int phi[N] , s[N] , prime[N] , vis[N] , tot , d[N] ;
    ll qmi(ll a , ll b , ll mod) {
    	ll res = 1 ;
    	while(b) {
    		if(b & 1) res = res * a % mod ;
    		b >>= 1 ;
    		a = a * a % mod ;
    	}
    	return res ;
    }
    map<int , ll> mp ;
    int Mod(ll n , int mod) {
    	return (n % mod + mod) % mod ;
    }
    int calc_S(int n , int mod) {
    	if(n < N) return s[n] ;
    	if(mp.count(n)) return mp[n] ;
    	int ans = Mod(1ll * n * (n + 1) / 2 , mod);
    	for(int l = 2 , r ;l <= n ;l = r + 1) {
    		r = n / (n / l) ;
    		ans = Mod((ans - 1ll * (r - l + 1) % mod * calc_S(n / l , mod ) % mod) % mod , mod)  ;
    	}
    	return mp[n] = ans ;
    }
    ll calc_d(int n , int mod) {
    	if(n < N) return d[n] ;
    	int ans = 0 ;
    	for(int l = 1 , r ; l <= n ; l = r + 1) {
    		r = n / (n / l) ;
    		int sum = ( 1ll * (r - l + 1) * (r + l) / 2 ) % mod ;
    		int pre = ( 1ll * (1 + n / l) * (n / l) / 2 ) % mod  ;
    		ans = Mod( ans + 1ll * sum * pre % mod , mod ) ;
    	}
    	return ans ;
    }
    void get_phi(int mod) {
    	phi[1] = 1 ;
    	for(int i = 2; i < N ; i ++ ) {
    		if(!vis[i]) prime[++ tot] = i , phi[i] = i - 1 ;
    		for(int j = 1 ; j <= tot && i * prime[j] < N ;j ++ ) {
    			vis[i * prime[j]] = 1 ;
    			if(i % prime[j] == 0) {
    				phi[i * prime[j]] = phi[i] * prime[j] ;
    				break ;
    			}
    			phi[i * prime[j]] = phi[i] * phi[prime[j]] ;
    		}
    	}
    	for(int i = 1; i < N ;i ++ ) 
    		for(int j = i ;j < N ;j += i ) 
    			 d[j] ++ ;
    	for(int i = 1; i < N ;i ++ ) {
    		d[i] = (d[i - 1] + 1ll * i * d[i] % mod) % mod ;
    		s[i] = (s[i - 1] + phi[i] % mod) % mod ;
    	}
    }
    int work()
    {
      cin >> n >> m >> p ;
      get_phi(p - 1) ;
      if(m % p == 0) return puts("0") , 0 ;
      ll ans = 0 ;
      for(int l = 1 , r ; l <= n ; l = r + 1) {
      	r = n / (n / l) ;
      	int dn = Mod(calc_d(r , p - 1) - calc_d(l - 1 , p - 1) , p - 1) ;
      	int sn = Mod(2ll * calc_S(n / l , p - 1) - 1, p - 1)  ;
      	ans = Mod(ans + 1ll * dn * sn , p - 1) ;
      }
      cout << qmi(m , ans , p) << "
    " ;
      return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
    
      work() ;
      return 0 ;
    }
    /*
    
    */
    	
    

    C - Angel's Journey

    计算几何

    先将b点变换到a的右边,根据中点对称

    其中这种情况,b在c的右边最小为len(b , c) + (弧长)ac = len(b , c) + pi/2 * r

    然后就是这种情况

    最短为弧ac + 弧ce + len(b , e)

    len(b , e)直接勾股定理

    弧ac r * pi / 2

    弧ce , 先根据a4求出a1 , 在直角三角形内求出a2,然后求出a3 r * a3

    vector<int> v ;
    int n , m ;
    PII a , b ;
    int r ;
    double sq(int x) {
    	return 1.0 * x * x ;
    }
    double len(PII a , PII b) {
    	return sqrt(sq(a.x - b.x) + sq(a.y - b.y)) ;
    }
    PII operator - (const PII &a , const PII &b) {
    	return {b.x - a.x , b.y - a.y} ;
    }
    double operator * (const PII &a , const PII &b) {
    	return a.x * b.x + a.y * b.y ;
    }
    double get(PII a , PII b) {
    	return acos(a * b / len(a , {0 , 0})) ;
    }
    int work()
    {
    	cin >> a.x >> a.y >> r >> b.x >> b.y ;
    	PII c = {a.x + r , a.y} ;
    	if(b.x <= a.x) b.x = 2 * a.x - b.x ;
    	double ans = pi / 2.0 * r ;
    	if(b.x >= c.x) ans += len(b , c) ;
    	else {
    		double ab = len(a , b) ;
    		ans += sqrt(ab * ab - 1.0 * r * r) ;
    		ans += 1.0 * r * ( pi / 2 - acos(1.0 * r / ab) - get(b - a , {0 , -1})) ;
    	}
    	printf("%.4lf
    " , ans) ;
    	return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
      int n ;
      cin >> n ;
      while(n -- )
      work() ;
      return 0 ;
    }
    /*
    
    */
    

    D - Miku and Generals

    首先构造图之后发现某些点是独立点,某些点在二分图内。先处理一边二分图,然后写成两个属性值{a , b} , 选手要么选a,要么选b

    对于独立点也是{a , a} ,

    然后直接写个二维01背包

    dp[j][2] 表示选择第n个的第几个属性能否组成j这个数值
      转移:
        dp[j][0] |= dp[j - a[i]][0]
        dp[j][0] |= dp[j - a[i]][1]
        dp[j][1] |= dp[j - a[i]][0]
        dp[j][1] |= dp[j - a[i]][1]
    
    int a[N] , n , m , vis[N] ;
    PII b[N] ;
    vector<int> v[N] ;
    int dp[N][2] , sum = 0 , res = 0 ;
    void dfs(int u , int f , int p) {
    	vis[u] = p ;
    	sum += a[u] ;
    	res += p * a[u] ;
    	for(auto x : v[u]) {
    		if(x == f || vis[x] != -1) continue ;
    		dfs(x , u , p ^ 1) ;
    	}
    }
    int work()
    {
      cin >> n >> m ;
      ll ans = 0 ;
      for(int i = 1; i <= n ;i ++ ) 
      	 cin >> a[i] , a[i] /= 100 , v[i].clear() , vis[i] = -1 , ans += a[i]  ;
      for(int i = 1; i <= m ;i ++ ) {
      	int x , y ;
      	cin >> x >> y ;
      	v[x].pb(y) , v[y].pb(x) ;
      }
      int cnt = 0 ;
      for(int i = 1; i <= n ;i ++ ) {
      	if(vis[i] != -1) continue ;
      	if(v[i].size() == 0) ++ cnt ,  b[cnt].x = b[cnt].y = a[i] , vis[i] = 2 ;
      	else sum = 0 , res = 0 , dfs(i , 0 , 0) , ++ cnt , b[cnt].x = res , b[cnt].y = sum - res ; 
      }
      for(int i = 1 ;i <= ans ;i ++ ) dp[i][0] = dp[i][1] = 0 ;
      dp[0][0] = dp[0][1] = 1 ;
      for(int i = 1; i <= cnt ;i ++ ) {
      	for(int j = ans ;j >= min(b[i].x , b[i].y) ;j -- ) {
      		if(j >= b[i].x)
    	  		dp[j][0] |= dp[j - b[i].x][0] ,
    	  	  	dp[j][1] |= dp[j - b[i].x][1] ;
      	  	if(j >= b[i].y)
    	  		dp[j][0] |= dp[j - b[i].y][0] ,
    	  		dp[j][1] |= dp[j - b[i].y][1] ;
      	}
      }
      for(int i = (ans + 1) / 2 ; i <= ans ; i ++ ) 
      	 if(dp[i][0] || dp[i][1]) {
      	 	cout << i * 100 << "
    " ;
      	 	return 0 ;
      	 }
      return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
      int n ;
      cin >> n ;
      while(n -- )
      work() ;
      return 0 ;
    }
    /*
    
    */
    

    E - Tree

    一看在树上对某个路径操作,并且操作频繁:树链剖分

    最终判断输赢,根据nim定理,如果所有值异或为0,则先手必输

    求路径异或和。

    对于 | 操作,发现对于每一位来说,,如果是0,不变,如果是1,那么路径上的所有的点权的当前位都被置为1

    对于 & 操作,发现对于每一位来说,,如果是1,不变,如果是0,那么路径上的所有的点权的当前位都被置为0

    那么直接对每一位建立一个线段树,维护区间变为1,变为0.求区间和。

    最后查询的时候对每一位进行查询,如果是奇数就加上(1 << i) ,最后再异或t。

    const double esp = 1e-6 , pi = acos(-1) ;
    typedef pair<int , int> PII ;
    const int N = 4e5 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
    int a[N] , son[N] , top[N] , id[N] , rk[N] , dep[N] , fa[N] , idx , sz[N] ;
    vector<int> v[N] ;
    struct node {
    	int sum[N] , lazy[N] ;
    	void build(int rt , int l , int r , int t) {
    		lazy[rt] = -1 ;
    		if(l == r) {
    			sum[rt] = (a[rk[l]] >> t & 1) ;
    			return ;
    		}
    		int mid = l + r >> 1 ;
    		build(ls , l , mid , t) ;
    		build(rs , mid + 1 , r , t) ;
    		sum[rt] = sum[ls] + sum[rs] ;
    	}
    	void down(int rt , int l , int r) {
    		if(lazy[rt] != -1) {
    			lazy[ls] = lazy[rs] = lazy[rt] ;
    			int mid = l + r >> 1 ;
    			sum[ls] = (mid - l + 1) * lazy[rt] ;
    			sum[rs] = (r - mid) * lazy[rt] ;
    			lazy[rt] = -1 ;
    		}
    	}
    	void update(int rt , int l , int r , int ql , int qr , int k) {
    		if(ql <= l && r <= qr) {
    			lazy[rt] = k ;
    			sum[rt] = (r - l + 1) * k ;
    			// down(rt , l , r) ;
    			return ;
    		}
    		down(rt , l , r) ;
    		int mid = l + r >> 1 ;
    		if(ql <= mid) update(ls , l , mid , ql , qr , k) ;
    		if(qr > mid) update(rs , mid + 1 , r , ql , qr , k) ;
    		sum[rt] = sum[ls] + sum[rs] ;
    	}
    	int ask(int rt , int l , int r , int ql , int qr ) {
    		if(ql <= l && r <= qr) return sum[rt] ;
    		down(rt , l , r) ;
    		int mid = l + r >> 1 , ans = 0 ;
    		if(ql <= mid) ans += ask(ls , l , mid , ql , qr) ;
    		if(qr > mid) ans += ask(rs , mid + 1 , r , ql , qr) ;
    		return ans ;
    	}
    }T[31] ;
    void dfs1(int u , int f) {
    	dep[u] = dep[f] + 1 ;
    	sz[u] = 1 ;
    	fa[u] = f ;
    	for(auto x : v[u])  {
    		if(x == f) continue ;
    		dfs1(x , u) ;
    		sz[u] += sz[x] ;
    		if(sz[son[u]] < sz[x]) son[u] = x ;
    	}
    }
    void dfs2(int u , int t) {
    	top[u] = t ;
    	id[u] = ++ idx ;
    	// cout << u << " " << id[u] << " ---- " << top[u] << "
    " ;
    	rk[idx] = u ;
    	if(son[u]) dfs2(son[u] , t) ;
    	for(auto x : v[u]) {
    		if(x == fa[u] || x == son[u]) continue ;
    		dfs2(x , x) ;
    	}
    }
    int work()
    {
      int n ,  q ;
      cin >> n >> q ;
      for(int i = 1; i <= n ;i ++ ) cin >> a[i] ;
      	// puts("S") ;
      for(int i = 1; i < n ;i ++ ) {
      	int x , y ;
      	cin >> x >> y ;
      	v[x].pb(y) , v[y].pb(x) ;
      }
      dfs1(1 , 0) ;
      dfs2(1 , 1) ;
      for(int i = 0 ;i < 31;i ++ )  
      	 T[i].build(1 , 1 , n , i) ;
      while(q -- ) {
      	int op , s , t ;
      	cin >> op >> s >> t; 
      	if(op == 1) {
      		while(s) {
      			for(int i = 0 ;i < 31; i ++ ) 
      				 if(t >> i & 1) 
      				 	T[i].update(1 , 1 , n , id[top[s]] , id[s] , 1) ;
      			s = fa[top[s]] ;
      		}
      	}else if(op == 2){
      		while(s) {
      			for(int i = 0 ;i < 31; i ++ )
      				 if((t >> i & 1) == 0)
      				 	 T[i].update(1 , 1 , n , id[top[s]] , id[s] , 0)  ; // , cout << i << " " << id[top[s]] << " +++ " << id[s] << "
    ";
      			s = fa[top[s]] ;
      		}
      	}else if(op == 3){
      		int res = 0 ;
      		while(s) {
      			for(int i = 0 ;i < 31 ;i ++ ){
      				// cout << i << " " << id[top[s]] << " " << id[s] << " " << T[i].ask(1 , 1 , n , id[top[s]] , id[s]) << "
    " ;
      				res ^= (1 << i) * (T[i].ask(1 , 1 , n , id[top[s]] , id[s]) % 2) ;
      			}
      			s = fa[top[s]] ;
      		}
      		// cout << res << "
    " ;
      		if(res == t) puts("NO") ;
      		else puts("YES") ;
      	}else {
      		for(int i = 0 ;i < 5; i ++ ) {
      			cout << i << " " << T[i].ask(1 , 1 , n , s , t) << "
    " ;
      		}
      	}
      }
      return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
    
      work() ;
      return 0 ;
    }
    /*
    12 13
    2 1 2 1 2 1 2 1 2 1 2 1
    1 2
    1 3
    2 4
    2 5
    4 8
    5 9 
    5 10
    3 6
    3 7
    6 11
    7 12
    2 12 4
    3 6 1
    1 10 2
    2 7 2
    3 6 1
    3 1 1
    2 6 1
    3 11 2
    */
    

    J - And And And

    只需要统计有多少路径异或位0,然后加上两端点分别向两边延申的个数相乘

    最关键的是怎么算贡献

    点分治

    对于每个重心节点,他的所有子树可能存在一个链向上

    比如重心节点2,那么此时对于1来说,和其他节点组合的贡献值是多少呢。 答案是1

    这里怎么求解的方法是个小技巧,

    对于重心节点向下的分支,他们的大小就是sz[u] , 但是存在一段从重心节点一直往上

    只需要在刚开始做遍遍历,求出每个节点的父亲节点。

    然后在对每个重心节点进行遍历子树的时候再进行求一次父亲节点,如果相同,就说明他是从上向下的,否则就是从下到上的。

    如果是从下到上的,那么他的延申节点就是n - sz[fa[u]] ,否则的话就是sz[u]

    然后难点基本就没有了。

    const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
    vector<PII> v[N] ;
    int n , m , sz[N] , vis[N] ;
    int rt , sum , res ;
    int get_rt(int u , int f) {
    	sz[u] = 1 ; 
    	int maxn = 0 ;
    	for(auto x : v[u]) {
    		if(x.x == f || vis[x.x]) continue ;
    		get_rt(x.x , u) ; 
    		sz[u] += sz[x.x] ;
    		maxn = max(maxn , sz[x.x]) ;
    	}
    	maxn = max(maxn , sum - sz[u]) ;
    	if(maxn < res) res = maxn , rt = u ;
    }
    int dep[N] , fa[N], s[N] ;
    void dfs(int u , int f) {
    	s[u] = 1 ;
    	fa[u] = f ;
    	for(auto x : v[u]) {
    		if(x.x == f) continue ;
    		dfs(x.x , u) ;
    		s[u] += s[x.x] ;
    	}
    }
    ll ans = 0 ;
    unordered_map<ll , ll> mp , c;
    int ff[N] ;
    void get_xor(int u , int f , ll w) {
    	ff[u] = f ;
    	if(f) c[u] = w ;
    	for(auto x : v[u]) {
    		if(x.x == f || vis[x.x]) continue ;
    		get_xor(x.x , u , w ^ x.y) ;
    	}
    }
    ll get_sz(int x) {
    	if(ff[x] == fa[x]) return s[x] ;
    	return (n - s[ff[x]]) ;
    }
    ll get(int x , int y) {
    	return 1ll * get_sz(x) * get_sz(y) % mod ;
    }
    void calc(int u) {
    	mp.clear() ;
    	for(auto x : v[u]) {
    		if(vis[x.x]) continue ;
    		c.clear() ;
    		get_xor(x.x , u , x.y) ;
    		int y = x.x ;
    		for(auto z : c) {
    			if(z.y == 0) {
    				if(fa[y] == u) ans = (ans + get_sz(z.x) * (n - s[y]) % mod) % mod ; 
    				else ans = (ans + get_sz(z.x) * s[u] % mod) % mod ; 
    			}
    			ans = (ans + mp[z.y] * get_sz(z.x) % mod) % mod ;
    		}
    		for(auto z : c) 
    			mp[z.y] = (mp[z.y] + get_sz(z.x)) % mod ; 
    		
    	}
    }
    void solve(int u) {
    	vis[u] = 1 ;
    	calc(u) ;
    	for(auto x : v[u]) {
    		if(vis[x.x]) continue ;
    		sum = res = sz[x.x] ;
    		get_rt(x.x , u) ;
    		solve(rt) ;
    	}
    }
    int work()
    {
    	cin >> n ;
    	for(int i = 2; i <= n ;i ++ ) {
    		int x ;
    		ll w ;
    		cin >> x >> w ;
    		v[i].emplace_back(x , w) ;
    		v[x].emplace_back(i , w) ;
    	}
    	dfs(1 , 0) ;
    	sum = res = n ;
    	get_rt(1 , 0) ;
    	solve(rt) ;
    
    	cout << ans << "
    " ;
    	return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
    
      work() ;
      return 0 ;
    }
    /*
    16
    1 2
    2 2
    3 1
    2 1
    5 1
    5 2
    5 4
    2 3
    9 3
    9 1
    1 1
    12 3
    12 2
    13 1
    13 2
    
    8
    1 2
    2 2
    3 1
    2 1
    5 1
    5 2
    5 4
    */
    

    dfs搜索

    先对整个树进行一个异或,dis[i] , 表示从根节点到i节点的异或和

    那么对于任意两个相同值dis的节点,即符合题目要求。

    遍历整个树,遍历到当前节点的时候,

    需要查找一下当前节点所有的祖先节点的值是否有相同的。

    还需要查找一下之前已经访问过的分支的dis值时候又相同的。

    访问完当前分支需要回溯,将当前点对其子树节点的贡献剪掉

    vector<PII> v[N] ;
    unordered_map<ll , int> mp ;
    ll dis[N] ;
    ll ans = 0 ;
    void dfs(int u , int f) {
    	sz[u] = 1 ;
    	for(auto x : v[u]) {
    		if(x.x == f) continue ;
    		dis[x.x] = dis[u] ^ x.y ;
    		dfs(x.x , u) ;
    		sz[u] += sz[x.x] ;
    	}
    }
    ll Mod(ll x) {
    	return (x % mod + mod) % mod ;
    }
    void dfs_calc(int u , int f) {
    //当前节点的总贡献
    	ans = Mod(ans + 1ll * sz[u] * mp[dis[u]] % mod) ;
    	for(auto x : v[u]) {
    		if(x.x == f) continue ;
    		//当前节点对孩子节点的贡献
    		mp[dis[u]] = Mod(mp[dis[u]] + 1ll * n - sz[x.x]);
    		dfs_calc(x.x , u) ;
    		// 回溯
    		mp[dis[u]] = Mod(mp[dis[u]] - 1ll * n + sz[x.x]);
    	}
    	// 当前分支对后面未访问分支的贡献
    	mp[dis[u]] = Mod(mp[dis[u]] + sz[u]) ;
    }
    int work()
    {
      cin >> n ;
      for(int i = 2; i <= n ;i ++ ) {
      	int x ;
      	ll y ;
      	cin >> x >> y ;
      	v[x].emplace_back(i , y) ;
      }
      dfs(1 , 0) ;
      dfs_calc(1 , 0) ;
      cout << ans << "
    " ;
      return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
    
      work() ;
      return 0 ;
    }
    /*
    
    */
    	
    

    L - Swap

    打表找规律

    M - Travel

    二分+01bfs,因为每增加一次的代价、扩张都是一样的。

    只需要二分一下扩展mid次,然后对于w[i] <= mid * d的边贡献都是1,否则都是0

    最后只需要判断一下最短路是否是mid * e

    vector<PII> v[N] ;
    bool check(ll d , ll e) {
    	vector<int> dis(n + 1) ;
    	vector<bool> vis(n + 1) ;
    	for(int i = 1; i <= n ;i ++ ) dis[i] = INF , vis[i] = 0 ;
    	dis[1] = 0 ;
    	priority_queue<PII , vector<PII> , greater<PII> > q ;
    	q.push({0 , 1}) ;
    	while(q.size()) {
    		int u = q.top().y ;
    		q.pop() ;
    		if(u == n) {
    			// cout << d << " " << e << " " << dis[u] << "
    " ;
    			return dis[u] <= e ;
    		}
    		if(vis[u]) continue ;
    		vis[u] = 1 ;
    		for(auto x : v[u]) {
    			if(x.y > d) continue ;
    			if(dis[x.x] > dis[u] + 1) {
    				dis[x.x] = dis[u] + 1 ;
    				if(dis[x.x] > e) continue ;
    				q.push({dis[x.x] , x.x}) ;
    			}
    		}
    	}
    	return dis[n] <= e ;
    }
    int work()
    {
      cin >> n >> m >> c >> d >> e ;
      for(int i = 1; i <= m ;i ++ ) {
      	int a , b , w ;
      	cin >> a >> b >> w ;
      	v[a].emplace_back(b , w) ;
      	v[b].emplace_back(a , w) ;
      }
      int l = 0 , r = INF , ans = 0 ;
      while(l <= r) {
      	int mid = l + r >> 1 ;
      	if(check(1ll * mid * d , 1ll * mid * e)) r = mid - 1 , ans = mid ;
      	else l = mid + 1 ;
      }
      // cout << ans << "
    " ;
      if(ans)
      	cout << 1ll * ans * c << "
    " ;
      else puts("-1") ;
      return 0 ;
    }
    int main()
    {
      //   freopen("C://Users//spnooyseed//Desktop//in.txt" , "r" , stdin) ;
      //   freopen("C://Users//spnooyseed//Desktop//out.txt" , "w" , stdout) ;
    
      work() ;
      return 0 ;
    }
    /*
    
    */
    
    每次做题提醒自己:题目到底有没有读懂,有没有分析彻底、算法够不够贪心、暴力够不够优雅。
  • 相关阅读:
    SQL随机生成6位数字
    安装时提示 INSTALL_PARSE_FAILED_MANIFEST_MALFORMED 解决办法
    Windows 7 完美安装 Visual C++ 6.0
    解决js中window.location.href不工作的问题
    DataList中动态显示DIV
    Gridview、DataList、Repeater获取行索引号
    Java多jdk安装
    【CentOS】samba服务器安装与配置
    【CentOS】IBM X3650M4 IMM远程管理【转载】
    【Java】Eclipse导出jar包与javadoc
  • 原文地址:https://www.cnblogs.com/spnooyseed/p/14785583.html
Copyright © 2020-2023  润新知