首页 > 其他分享 >「NOI2021 D1T3 庆典」题解

「NOI2021 D1T3 庆典」题解

时间:2024-09-10 20:04:02浏览次数:1  
标签:DAG int 题解 NOI2021 dfn low id D1T3 Rightarrow

uoj675

加强:\(\sum k\le6\times10^5\)


暴力

\(u\) 在 \(s\Rightarrow t\) 路径上 \(\iff\) 正图上 \(s\Rightarrow u\) 且反图上 \(u\Rightarrow t\)

时间复杂度 \(O((n+m)q)\)

正解

只关心可达性,不妨 SCC 缩点成 DAG。注意到一个奇怪的条件:

对于三座城市 \(x,y,z\),若 \(x\Rightarrow z\) 且 \(y\Rightarrow z\),那么有 \(x\Rightarrow y\) 或 \(y\Rightarrow x\)。

若存在边 \((x,z),(x,y),(y,z)\),可以删去边 \((x,z)\)。这样每个点的前驱唯一,又有原图弱连通,即为叶向树
实现上有两种做法:拓扑排序,保留每个点的最后一条入边;从零入度点开始 dfs,优先递归编号大的儿子,保留 dfs 树(利用 tarjan 缩点的性质)

叶向树

\(k=0\) \(t\) 在 \(s\) 子树内时答案为深度差

\(k=1\) 只需要讨论走不走新边,转化为子树和根链求交

\(k=2\)

时间复杂度 \(O(n\log n+\sum k\log k)\)

#ifndef FT
	#define NDEBUG
#endif
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std; using namespace __gnu_pbds; using namespace __gnu_cxx;
#define For(i,x,y,...) for(int i=x,##__VA_ARGS__;i<=(y);++i)
#define rFor(i,x,y,...) for(int i=x,##__VA_ARGS__;i>=(y);--i)
#define Rep(i,x,y,...) for(int i=x,##__VA_ARGS__;i<(y);++i)
#define pb emplace_back
#define sz(a) int((a).size())
#define all(a) (a).begin(),(a).end()
#define fi first
#define se second
#define mkp make_pair
typedef long long LL; typedef vector<int> Vi; typedef pair<int,int> Pii;
auto ckmax=[](auto &x,auto y) { return x<y ? x=y,true : false; };
auto ckmin=[](auto &x,auto y) { return y<x ? x=y,true : false; };
sfmt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l,int r) { return uniform_int_distribution<>(l,r)(mt); }
template<typename T=int>T read() { T x; cin>>x; return x; }

const int mod = 998244353;
struct mint {
	int x; mint(int x=0):x(x<0?x+mod:x<mod?x:x-mod){}
	mint(LL y) { y%=mod, x=y<0?y+mod:y; }
	mint& operator += (const mint &y) { x=x+y.x<mod?x+y.x:x+y.x-mod; return *this; }
	mint& operator -= (const mint &y) { x=x<y.x?x-y.x+mod:x-y.x; return *this; }
	mint& operator *= (const mint &y) { x=1ll*x*y.x%mod; return *this; }
	friend mint operator + (mint x,const mint &y) { return x+=y; }
	friend mint operator - (mint x,const mint &y) { return x-=y; }
	friend mint operator * (mint x,const mint &y) { return x*=y; }
};	mint Pow(mint x,LL y=mod-2) { mint z(1);for(;y;y>>=1,x*=x)if(y&1)z*=x;return z; }

const int N = 3e5+5;

namespace T { // 叶向树
int n,rt,ind,val[N],sum[N],dfn[N],st[19][N];
Vi e[N];
int _min(int x,int y) { return dfn[x]<dfn[y] ? x : y; }
void dfs(int u,int fa) {
	dfn[u] = ++ind, st[0][ind] = fa, sum[u] = sum[fa] + val[u];
	for(int v : e[u]) dfs(v,u);
}
int lca(int u,int v) {
	if( u == v ) return u;
	if( (u=dfn[u]) > (v=dfn[v]) ) swap(u,v);
	int k = __lg(v-u++);
	return _min(st[k][u], st[k][v-(1<<k)+1]);
}
void main() {
	// cerr<<"T:\n"; For(i,1,n) for(int j : e[i]) cerr<<"  "<<i<<" "<<j<<'\n';
	dfs(rt,0);
	For(i,1,18) For(j,1,n-(1<<i)+1) st[i][j] = _min(st[i-1][j], st[i-1][j+(1<<i-1)]);
}
}

namespace DAG {
int n,deg[N];
Vi e[N];
void main() {
	// cerr<<"DAG:\n"; For(i,1,n) for(int j : e[i]) cerr<<"  "<<i<<" "<<j<<'\n';
	T::n = n;
	For(i,1,n) for(int j : e[i]) ++deg[j];
	queue<int> q;
	For(i,1,n) if( !deg[i] ) q.emplace(i), T::rt = i;
	assert(sz(q)==1);
	while( sz(q) ) {
		int u = q.front(); q.pop();
		for(int v : e[u]) if( !--deg[v] ) q.emplace(v), T::e[u].pb(v);
	}
}
}

namespace G { // 原图
int n,m,ind,dfn[N],low[N],id[N];
Vi e[N];
void tj(int u) {
	static int t,s[N];
	dfn[u] = low[u] = ++ind, id[u] = -1, s[++t] = u;
	for(int v : e[u])
		if( !dfn[v] ) tj(v), ckmin(low[u], low[v]);
		else if( !~id[v] ) ckmin(low[u], dfn[v]);
	if( dfn[u] == low[u] ) {
		++DAG::n;
		do id[s[t]] = DAG::n, ++T::val[DAG::n]; while( u != s[t--] );
	}
}
void main() {
	For(i,1,n) if( !dfn[i] ) tj(i);
	For(i,1,n) for(int j : e[i]) if( id[i] != id[j] ) DAG::e[id[i]].pb(id[j]);
	// cerr<<"id: "; For(i,1,n) cerr<<id[i]<<" "; cerr<<'\n';
}
}

int mm;
Vi ver;
struct VT {
	int head[N];
	bool vis[N];
	struct { int nxt,to; } e[30];
	void clr() { for(int i : ver) head[i] = vis[i] = 0; }
	void dfs(int u) {
		if( vis[u] ) return; vis[u] = 1;
		for(int i = head[u], v; v = e[i].to, i; i = e[i].nxt) dfs(v);
	}
} g1,g2;
void adde(int x,int y) {
	g1.e[++mm] = {g1.head[x],y}, g1.head[x] = mm;
	g2.e[mm] = {g2.head[y],x}, g2.head[y] = mm;
	// cerr<<"  "<<x<<" "<<y<<" "<<'\n';
}

void MAIN() {
	int q,k; cin>>G::n>>G::m>>q>>k; For(i,1,G::m, x,y) cin>>x>>y, G::e[x].pb(y);
	G::main(), DAG::main(), T::main();
	while( q-- ) {
		// cerr<<"VT:\n";
		int s,t; s = G::id[read()], t = G::id[read()], ver = {s,t};
		For(i,1,k, x,y)
			x = G::id[read()], y = G::id[read()],
			ver.pb(x), ver.pb(y), adde(x,y);
		auto cmp = [](int x,int y){return T::dfn[x]<T::dfn[y];};
		sort(all(ver),cmp), ver.erase(unique(all(ver)),ver.end());
		rFor(i,sz(ver)-1,1) ver.pb(T::lca(ver[i-1],ver[i]));
		ver.pb(T::rt), sort(all(ver),cmp), ver.erase(unique(all(ver)),ver.end());
		Rep(i,1,sz(ver)) adde(T::lca(ver[i-1],ver[i]),ver[i]);
		g1.dfs(s), g2.dfs(t);
		int ans = 0;
		for(int i : ver) if( g1.vis[i] && g2.vis[i] ) ans += T::val[i];
		For(i,k+1,mm) if(int x = g2.e[i].to, y = g1.e[i].to; g1.vis[x] && g2.vis[y] )
			ans += T::sum[T::st[0][T::dfn[y]]] - T::sum[x];
		cout<<ans<<'\n';
		g1.clr(), g2.clr(), mm = 0;
	}
} signed main() {
#ifdef FT
	freopen("in","r",stdin); freopen("out","w",stdout);
#endif
	ios::sync_with_stdio(0);cin.tie(0);
	int lft=1; while( lft-- ) {
		MAIN();
	}
	return 0;
}

标签:DAG,int,题解,NOI2021,dfn,low,id,D1T3,Rightarrow
From: https://www.cnblogs.com/ft61/p/18405507

相关文章

  • 题解:[USACO07DEC] Sightseeing Cows G
    洛谷P2868题目大意有个nnn个点,mmm条边的有向图,点有点权,边有边......
  • 力扣474-一和零(Java详细题解)
    题目链接:474.一和零-力扣(LeetCode)前情提要:因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。最近刚学完01背包,所以现在的题解都是以01背包问题为基础再来写的。如果大家不懂01背包的话,建议可以去学一学,01背包问题可以说是背包问题的基础。如果大家感兴趣,......
  • luogu2516题解
    随机说话第一次交的时候写的版本是这个。下面在sum的计算上少了个else,遂出错。以后写的时候还是尽量简单点,主要是调试的时候好调。题目分析题目里面的公共子序列就是可以不连续可以为空的在字符串里出现顺序相同的子串。对于一个公共子序列,在找到最后一个公共的字符的时......
  • P4563 [JXOI2018] 守卫 题解
    P4563[JXOI2018]守卫题解不愧是九条可怜的\(\text{JXOI}\),只能说确实是道好题。假设当前我们在求\([l,r]\),我们不难发现\(r\)端点一定要放保镖,于是考虑\(r\)保镖的最大监视范围\([x,r]\)。由题意得到对于\([x,r]\)中的每个\(p_1,p_2,\cdots,p_k\),要求斜率\(t(p_i,......
  • 树上一些点的选 题解
    题意简述给你一棵\(n\)个节点以\(1\)为根的有根树,和一个整数\(m\)。对于树上每一个点\(u\),有三个权值\(X,Y,Z\)。你需要在\(u\)的祖先里(不含\(u\))中选出至少\(X\)个点,记\(S_1\)表示这些点到\(u\)的距离之和;在\(u\)的后代里(不含\(u\))中选出至少\(Y\)个点,......
  • CF717A Festival Organization 题解
    Description一个合法的串定义为:长度在\([l,r]\)之间,且只含0,1,并且不存在连续\(2\)个或更多的\(0\)。现在要选出\(k\)个长度相同的合法的串,问有几种选法,答案模\(10^9+7\)。\(1\leqk\leq200,1\leql\leqr\leq10^{18}\)。Solution容易发现答案为\(\sum_{i=l+2}^{r......
  • 【Py/Java/C++三种语言OD独家2024E卷真题】20天拿下华为OD笔试之【回溯】2024E-字符串
    可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳od1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例一输入输出说明示例二输入输出说明解题思路代码pythonjavacpp时空复杂度华为OD算法/大厂面......
  • Android中SurfaceView的双缓冲机制和普通View叠加问题解决办法
    本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点SurfaceView是Android平台上用于高效渲染图形的视图控件。它将内容绘制在一个独立的Surface上,可以直接由渲染线程访问,从而提高性能,尤其是在需要频繁刷新和更新......
  • 题解:CF913C Party Lemonade
    分析因为容量为\(2^{i-1}\),所以对于任意的\(i<j\),第\(j\)种瓶子一定可以通过选择\(2^{j-i}\)个\(i\)种瓶子来实现。定义一个瓶子的性价比为\(\dfrac{\textrm{容量}}{\textrm{价格}}\),即\(\dfrac{2^{i-1}}{c_i}\)。我们可以按照每个瓶子的性价比从高到低排序,依次选择......
  • 题解:CF913D Too Easy Problems
    题意给定一场考试,考试会持续\(T\)毫秒,由\(n\)道题目组成,你可以用\(t_i\)毫秒解决第\(i\)个问题,每个问题给定一个整数\(a_i\)。要求你选出一个试题集合\(S\),若该集合大小为\(k\),它应满足\(T\geq\sum_{i\inS}\limitst_i\),你需要最大化\(\sum_{i\inS}\limits[a_i......