首页 > 其他分享 >【UR #1】跳蚤国王下江南

【UR #1】跳蚤国王下江南

时间:2023-01-22 15:34:02浏览次数:56  
标签:now const int tot UR mod size 国王 跳蚤

题目链接

【UR #1】跳蚤国王下江南

做法

建出圆方树,考虑行走的路径一定是树形结构,每个环都有一个节点深度是最浅的,遇到环时会有两种走法。考虑DP。令 $ dp[i][j] $ 表示 $ i $ 往下走 $ j $ 步的方案数,可以由 $ i $ 在圆方树上的儿子节点转移(方点和圆点要分别讨论),最后求的是 $ dp[1][j] $ ,可以用生成函数计算。
考虑点分治转移DP。对于一个连通块,取离1号点最近的节点 root 作为连通块的根节点。取连通块的中心 g , 计算 g 的子树(包括 g)对 root 的转移。转移包括求 $ dp[g] $ 和 g 到 root 的方案数 $ f[g] $ ,进行卷积。先将这个连通块中的 g 去掉,递归求出原连通块里与 g 相邻的连通块的答案,再将 $ dp[g] $ 通过 g 的儿子节点求出,$ f[g] $ 可由 g 父亲所属的连通块转移得到。
由于点分治一次至少将点集划为一半,圆方树的点数 $ O(n) $ ,则递归时间复杂度 $ T(n) = 2T(\frac{n}{2}) + O(n \log n) = O(n \log^2 n) $ ,其中 $ f[g] $ 转移的时间 $ t(n) = t(n / 2) + O(n \log n) = O(n \log n) $ 。

#include <bits/stdc++.h>
#define fst first
#define snd second
#define pb push_back
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int mod = 998244353;

inline int add(const int &x, const int &y) { return x + y < mod ? x + y : x + y - mod; }
inline int sub(const int &x, const int &y) { return x - y < 0 ? x - y + mod : x - y; }
inline int mul(const int &x, const int &y) { return (int)((ll)x * y % mod); }
inline int ksm(int x, int y = mod - 2) {
	int ss = 1; for(; y; y >>= 1, x = mul(x, x)) if(y & 1) ss = mul(ss, x);
	return ss;
}
inline int Get(int x) { int ss = 1; for(; ss <= x; ss <<= 1); return ss; }
namespace Poly {
	inline int Get(int x) { int ss = 1; for(; ss <= x; ss <<= 1); return ss; }
	void ntt(vector<int> &A, int lmt, int opt) {
		A.resize(lmt + 5);
		for(int i = 0, j = 0; i < lmt; i++) {
			if(i > j) swap(A[i], A[j]);
			for(int k = lmt >> 1; (j ^= k) < k; k >>= 1);
		}
		vector<int> w(lmt >> 1);
		for(int mid = 1; mid < lmt; mid <<= 1) {
			w[0] = 1;
			int w0 = ksm(opt == 1 ? 3 : (mod + 1) / 3, (mod - 1) / mid / 2);
			for(int j = 1; j < mid; j++) w[j] = mul(w[j - 1], w0);
			for(int R = mid << 1, j = 0; j < lmt; j += R)
				for(int k = 0; k < mid; k++) {
					int x = A[j + k], y = mul(w[k], A[j + mid + k]);
					A[j + k] = add(x, y), A[j + mid + k] = sub(x, y);
				}
		}
		if(opt == -1)
			for(int i = 0, inv = ksm(lmt); i < lmt; i++) A[i] = mul(A[i], inv);
	}
	vector<int> Mul(const vector<int> &a, const vector<int> &b) {
		vector<int> A = a, B = b; int lmt = Get(a.size() + b.size() - 2);
		ntt(A, lmt, 1), ntt(B, lmt, 1);
		for(int i = 0; i < lmt; i++) A[i] = mul(A[i], B[i]);
		ntt(A, lmt, -1); return A.resize(a.size() + b.size() - 1), A;
	}
	vector<int> Add(const vector<int> &a, const vector<int> &b) {
		vector<int> A = a, B = b; int len = max(a.size(), b.size());
		A.resize(len), B.resize(len);
		for(int i = 0; i < len; i++) A[i] = (A[i] + B[i]) % mod;
		return A;
	}
}
using Poly::Mul;
using Poly::Add;

const int N = 300010;
int n, m;
vector<pii> E[N]; vector<int> e[N];
map<int, int> ma[N];

int dfn[N], low[N], idx = 0, sta[N], top = 0;
int tot = 0, posb[N], len[N], father[N], type[N];
// type == 1 : not belong to the circle above
void get_circle(int u, int v) {
	++tot;
	for(;;) {
		int x = sta[top--];
		e[x].pb(tot), e[tot].pb(x);
		++len[tot], posb[x] = len[tot], father[x] = tot;
		if(x == v) break;
	}
	type[v] = (len[tot] == 1 && ma[u][v] == 1);
	e[u].pb(tot), e[tot].pb(u);
	++len[tot], father[tot] = u;
}
void tarjan(int u, int ff) {
	dfn[u] = low[u] = ++idx, sta[++top] = u;
	for(auto v : E[u]) if(v.snd != ff) {
		if(!dfn[v.fst]) {
			tarjan(v.fst, v.snd), low[u] = min(low[v.fst], low[u]);
			if(low[v.fst] >= dfn[u]) get_circle(u, v.fst);
		}
		else low[u] = min(low[u], dfn[v.fst]);
	}
}

bool vis[N];
int sz[N], mx[N], anc[N], root, size;
vector<int> f[N], g[N], t;


int get_size(int u, int ff) {
	int res = 1;
	for(auto v : e[u]) if(v != ff && !vis[v]) res += get_size(v, u);
	return res;
}
void getrt(int u, int ff) {
	sz[u] = 1, mx[u] = 0;
	for(auto v : e[u]) if(v != ff && !vis[v]) {
		getrt(v, u), sz[u] += sz[v];
		mx[u] = max(mx[u], sz[v]);
	}
	mx[u] = max(mx[u], size - sz[u]);
	if(!root || mx[u] < mx[root]) root = u;
}
void solve_point(int u) {
	t.resize(1, 1);
	for(auto v : e[u]) if(father[u] != v && !vis[v]) t = Add(t, f[v]);
}
void solve_circle(int u) {
	for(auto v : e[u]) if(father[u] != v && !vis[v]) {
		int dis1 = len[u] - posb[v], dis2 = posb[v];
		t.resize(max(t.size(), f[v].size() + max(dis1, dis2)));
		for(int i = 0; i < (int)f[v].size(); i++) {
			t[i + dis1] = (t[i + dis1] + f[v][i]) % mod;
			if(!type[v]) t[i + dis2] = (t[i + dis2] + f[v][i]) % mod;
		}
	}
}
void jump(int u, int v) {
	// link v -> father[v]
	if(v > n) return ;
	int dis1 = len[father[v]] - posb[v], dis2 = posb[v];
	g[u].resize(max(g[u].size(), g[u].size() + max(dis1, dis2)));
	for(int i = g[u].size() - 1; ~i; i--) {
		g[u][i] = 0;
		if(i >= dis1) g[u][i] = (g[u][i] + g[u][i - dis1]) % mod;
		if(i >= dis2 && !type[v]) g[u][i] = (g[u][i] + g[u][i - dis2]) % mod;
	}
}
void build(int u) {
	root = 0, size = get_size(u, 0), getrt(u, 0);
	int now = root; vis[now] = 1, anc[now] = u;
//	printf(">>> %d -> %d\n", now, u);
	if(now != u) build(u);
	for(auto v : e[now]) if(!vis[v] && father[now] != v) {
		build(v);
	}
	t.clear();
	if(now <= n) solve_point(now);
	else solve_circle(now);
	g[now].resize(1, 1);
	
	for(int x = now; x != u; x = anc[father[x]]) {
		jump(now, x), g[now] = Mul(g[now], g[father[x]]);
	}
	vector<int> tmp = Mul(t, g[now]);
//	printf(">>> %d -> %d : \n", now, u);
//	printf(">>> "); for(auto v : t) printf("%d ", v); puts("");
//	printf(">>> "); for(auto v : g[now]) printf("%d ", v); puts("");
//	puts("");
	f[u] = Add(f[u], tmp);
	vis[now] = 0;
}
int main() {
	scanf("%d%d", &n, &m), tot = n;
	for(int i = 1; i <= m; i++) {
		int x, y; scanf("%d%d", &x, &y);
		E[x].pb({y, i}), E[y].pb({x, i}), ++ma[x][y], ++ma[y][x];
	}
	tarjan(1, 0);
//	for(int i = 1; i <= tot; i++) printf("father[%d] = %d : %d %d\n", i, father[i], len[i], posb[i]);
	build(1);
	f[1].resize(n);
	for(int i = 1; i < n; i++) printf("%d\n", f[1][i]);
	return 0;
}

标签:now,const,int,tot,UR,mod,size,国王,跳蚤
From: https://www.cnblogs.com/daniel14311531/p/17064460.html

相关文章

  • 1583_AURIX_TC275_SMU的控制以及FSP
    全部学习汇总:​​GreyZhang/g_TC275:happyhackingforTC275!(github.com)​​SMU的软件控制接口主要是实现了一些控制命令,用于控制SMU的状态机以及FSP。具体的内容在上......
  • 1584_AURIX_TC275_SMU的调试以及部分寄存器
    全部学习汇总:​​GreyZhang/g_TC275:happyhackingforTC275!(github.com)​​前面学习的过程中,突然间减速了不少。但是为了保证学习的推进,还是得有每天的稳定输出。我......
  • 1588_AURIX_TC275_PMU简介
    全部学习汇总:​​GreyZhang/g_TC275:happyhackingforTC275!(github.com)​​PMU是编程存储单元的缩写,但是落实到了具体的硬件模块上其实是一个Flash模块。在TC275中,只......
  • 1580_AURIX_TC275_SMU模块初步
    全部学习汇总:​​GreyZhang/g_TC275:happyhackingforTC275!(github.com)​​SMU集中了所有软硬件的Alarm信息,这个在之前的很多模块的描述中看得出来的。默认情况下,其......
  • Embedded Architecture
    词汇:ReactivesystemsMicroprocessor微处理器(CPU)MicroControllerUnit(MCU)eg.ARMbaremetal裸机Parallelism并行Multicore多核DSP数字信号处理器S......
  • Web安全入门与靶场实战(12)- 统一资源定位符URL
    互联网中存在着无数的Web站点,在每个站点中都存放着大量的Web资源,那系统该如何区分用户准备访问的是哪个站点中的哪个资源呢?比如在Linux系统中我们要对某个文件进行操作,首先......
  • c++ return
    return本是上是一个拷贝过程,不过是右值拷贝,也就是无标记变量的拷贝。不管是返回指针还是返回值,return首先将要return的值存到eax寄存器中,回到父函数再将返回的值赋给变量......
  • Spring Security & Spring Jpa
    ......
  • 【五期李伟平】CCF-C(CC'19)Privacy preserving distributed data mining based on secu
    JunLiu,YuanTian,YuZhouetal."Privacypreservingdistributeddataminingbasedonsecuremulti-partycomputation."ComputerCommunications.Ed.2020.20......
  • HGAME 2023 Week2 Pwn YukkuriSay题解
    HGAME2023Week2PwnYukkuriSay题解检查保护:拿到文件先checksec一下:64位程序,开启canary和nx保护,没有开启PIE(可以使用绝对地址了)继续往下看,先不着急打开ida,我们先运......