zoukankan      html  css  js  c++  java
  • 【Wannafly挑战赛24E】旅行

    【Wannafly挑战赛24E】旅行

    题面

    牛客

    题解

    首先有一个非常显然的(dp):我们直接把(s ightarrow t)的路径抠出来然后设(f_{i,j})表示到第(i)个点,目前余数为(j)的方案数。

    但是这样子复杂度显然是不对的,我们想办法快速合并对于某个点(u)(s ightarrow u)(t ightarrow u)的答案。

    一般这个点(u)都是(lca(s,t))但是我们这道题有一个特别神仙的思路就是将这个点(u)设为(s,t)在点分树上的(lca)

    我们对一组询问,找到(s,t)在点分树上的(lca)(u)并将询问离线,在点分治到(u)时处理询问,那么我们对于(u)的询问,(u)到所在的这个分治树中间所有点的(dp)值我们都是可以求出来的,这样子我们就可以合并答案了。

    最后的复杂度是点分治算路径的复杂度和回答询问复杂度,为(O(knlog n+qk))

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring> 
    #include <cmath> 
    #include <algorithm>
    using namespace std; 
    inline int gi() {
        register int data = 0, w = 1;
        register char ch = 0;
        while (!isdigit(ch) && ch != '-') ch = getchar(); 
        if (ch == '-') w = -1, ch = getchar(); 
        while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
        return w * data; 
    } 
    const int Mod = 998244353; 
    const int MAX_N = 2e5 + 5; 
    struct Graph { int to, next; } e[MAX_N << 1]; 
    int fir[MAX_N], e_cnt; 
    void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; } 
    void Add_Edge(int u, int v) { e[e_cnt] = (Graph){v, fir[u]}, fir[u] = e_cnt++; } 
    int N, K, Q, a[MAX_N]; 
    int Root, Siz, Rmx, size[MAX_N], dep[MAX_N], fa[MAX_N]; 
    bool used[MAX_N]; 
    void getRoot(int x, int fa) { 
    	size[x] = 1; 
    	int mx = 0; 
    	for (int i = fir[x]; ~i; i = e[i].next) { 
    		int v = e[i].to; if (v == fa || used[v]) continue; 
    		getRoot(v, x); 
    		size[x] += size[v]; 
    		mx = max(mx, size[v]); 
    	} 
    	mx = max(Siz - size[x], mx); 
    	if (mx < Rmx) Rmx = mx, Root = x; 
    }
    struct Query { int x, y, id; } ; 
    vector<Query> vec[MAX_N]; 
    int f[MAX_N][50], ans[MAX_N]; 
    void dfs(int x, int fa) { 
    	for (int i = fir[x]; ~i; i = e[i].next) { 
    		int v = e[i].to; if (v == fa || used[v]) continue; 
    		for (int j = 0; j < K; j++) f[v][j] = f[x][j]; 
    		for (int j = 0; j < K; j++) (f[v][(j + a[v]) % K] += f[x][j]) %= Mod; 
    		dfs(v, x); 
    	} 
    } 
    void Div(int x, int op) { 
    	used[x] = 1; 
    	if (op) { 
    		for (int i = 0; i < K; i++) f[x][i] = 0; 
    		f[x][0] = 1; 
    		dfs(x, 0); 
    		for (auto i : vec[x]) { 
    			int tmp[50]; 
    			for (int j = 0; j < K; j++) tmp[j] = f[i.x][j]; 
    			for (int j = 0; j < K; j++) (tmp[(j + a[x]) % K] += f[i.x][j]) %= Mod; 
    			for (int j = 0; j < K; j++) 
    				ans[i.id] = (ans[i.id] + 1ll * tmp[j] * f[i.y][(K - j) % K]) % Mod; 
    		} 
    	} 
    	for (int i = fir[x]; ~i; i = e[i].next) { 
    		int v = e[i].to; if (used[v]) continue; 
    		Siz = Rmx = size[v]; 
    		getRoot(v, x); 
    		dep[Root] = dep[x] + 1; 
    		fa[Root] = x; 
    		Div(Root, op); 
    	} 
    } 
    int LCA(int x, int y) { 
    	while (x != y) { 
    		if (dep[x] < dep[y]) swap(x, y); 
    		x = fa[x]; 
    	} 
    	return x; 
    } 
    
    int main () { 
    	clearGraph(); 
    	N = gi(), K = gi(); 
    	for (int i = 1; i < N; i++) { 
    		int u = gi(), v = gi(); 
    		Add_Edge(u, v), Add_Edge(v, u); 
    	} 
    	Siz = Rmx = N; 
    	getRoot(1, 0); 
    	Div(Root, 0); 
    	for (int i = 1; i <= N; i++) a[i] = gi() % K; 
    	Q = gi(); 
    	for (int i = 1; i <= Q; i++) { 
    		int x = gi(), y = gi(); 
    		vec[LCA(x, y)].push_back((Query){x, y, i}); 
    	} 
    	memset(used, 0, sizeof(used)); 
    	Siz = Rmx = N; 
    	getRoot(1, 0); 
    	Div(Root, 1); 
    	for (int i = 1; i <= Q; i++) printf("%d
    ", ans[i]); 
        return 0; 
    } 
    
  • 相关阅读:
    格式化数字,将字符串格式的数字,如:1000000 改为 1 000 000 这种展示方式
    jquery图片裁剪插件
    前端开发采坑之安卓和ios的兼容问题
    页面消息提示,上下滚动
    可以使用css的方式让input不能输入文字吗?
    智慧农村“三网合一”云平台测绘 大数据 农业 信息平台 应急
    三维虚拟城市平台测绘 大数据 规划 三维 信息平台 智慧城市
    农业大数据“一张图”平台测绘 大数据 房产 国土 农业 信息平台
    应急管理管理局安全生产预警平台应急管理系统不动产登记 测绘 大数据 规划 科教 三维 信息平台
    地下综合管廊管理平台测绘 大数据 地下管线 三维 信息平台
  • 原文地址:https://www.cnblogs.com/heyujun/p/11711538.html
Copyright © 2011-2022 走看看