神秘做题记录4

P6378 [PA 2010] Riddle

发现限制有两种:

  • 每条边的一个端点不选,另一个必须选。
  • 每个集合内的一个点选了,其他点不能选

这个题给定了无向图,很难直接刻画dp之类的算法,考虑对这个限制进行图论建模。

\(\forall u,v(u,v之间有边),\neg u \Rightarrow v,\neg v \Rightarrow u\)

\(\forall u,v \in S,u \Rightarrow \neg v\)

经典的2SAT问题,跑tarjan,获得了92分。

错误是RE,发现第二种限制的边是 \(n^2\) 量级的,所以还不等TLE空间就不够了。

点的个数是 \(2e6\) 量级,线段树优化建图都做不了,必须要优化到 \(O(n)\) 量级。

发现第二种限制其实就是一个点连前缀和后缀。

于是类似线段树优化建图,使用“前缀和优化建图”,具体是怎么样呢?

P6378

看图就明白了,pre和suf其实是分开的,pre之间连边就构成了传递性,suf同理。

这样的点数翻倍,边也会多很多但是还是 \(O(n)\) 的比线段树优化建图更优。

#include<bits/stdc++.h>
using namespace std;
const int N = 4000010,M = 40000010;
int n,m,k;
int a[N],pre[N],suf[N]; 
struct Edge{int to,nxt;}e[M];
int head[N],idx = 0;
void addedge(int u,int v){
	e[++idx] = {v , head[u]};
	head[u] = idx;
}
int dfn[N],low[N],bel[N],inst[N];
int id = 0,scc_cnt = 0,cnt;
stack<int> st;
void tarjan(int x){
	low[x] = dfn[x] = ++id;
	inst[x] = 1;
	st.push(x);
	for(int i = head[x];i;i = e[i].nxt){
		int h = e[i].to;
		if(!dfn[h]){
			tarjan(h);
			low[x] = min(low[x] , low[h]);
		}else if(inst[h]){
			low[x] = min(low[x] , low[h]);
		}
	}
	if(dfn[x] != low[x])return;
	int y;
	scc_cnt++;
	do{
		y = st.top();
		st.pop();
		bel[y] = scc_cnt;
		inst[y] = 0;
	}while(y != x);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0); 
	cin>>n>>m>>k;
	for(int i = 1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		addedge(u + n , v);
		addedge(v + n , u);
	}
	cnt = 2 * n;
	for(int i = 1;i<=k;i++){
		int sz;
		cin>>sz;
		for(int j = 1;j<=sz;j++){
			cin>>a[j];
			pre[j] = ++cnt,suf[j] = ++cnt;
			addedge(a[j] , pre[j]);
			addedge(suf[j] , a[j] + n);
		}
		for(int j = 1;j<sz;j++){
			addedge(pre[j] , pre[j + 1]);
			addedge(suf[j + 1] , suf[j]);
			addedge(a[j + 1] , suf[j]);
			addedge(pre[j] , a[j + 1] + n);
		}
	}
	for(int i = 1;i<=n * 2;i++){
		if(!dfn[i])tarjan(i);
	}
	bool fl = true;
	for(int i = 1;i<=n;i++){
		assert((bel[i] != 0) && (bel[i + n] != 0));
		if(bel[i] == bel[i + n])fl = false;
	}
	if(fl)cout<<"TAK\n";
	else cout<<"NIE\n";
	return 0;
}

P6939 [ICPC 2017 WF] Tarot Sham Boast

我的题解

[题解:P6939 ICPC 2017 WF] Tarot Sham Boast - 洛谷专栏

P6478 [NOI Online #2 提高组] 游戏

发现问题转化为两个点配对,直接树背包设 \(dp_{i , j}\) 为以 \(i\) 为根的子树内配对 \(j\) 次的方案数。

转移好做,只要判断每个点的颜色,那么子树内另一个颜色的点剩几个好求。

我以为做完了去看题解确认一下,果然假了,要套二项式反演。

这个好像还挺显然,因为剩下的点也要配对,方案乘进去即可,但是不能保证剩下的配对没用祖先关系,所以反演一下。

这就做完了,它的削弱版 P4859 就是在链上的,更加简单,紫题 += 2。

P15649 [省选联考 2026] 找寻者

切了大部分,主要是卡常部分用了 GPT 提示。

因为半年前我想了三四个小时,所以一下就能切入题意,我发现重要的是子树的链多长。

于是设 \(dp_{i,j}\) 为 \(i\) 往下延伸的链长为 \(j\) 的概率,且不说转移,这个怎么计算答案?

我们考虑一条轻链的贡献,显然是 \(sz_v\) 对吧,这个就拆开了,我当时考场上连这个都不会。

所以知道是轻链的概率答案好求,知道是轻链的概率就免不了求这个dp。

考虑进行树背包,随便转移,设 \(f_i\) 表示当前节点儿子所有链总长为 \(i\) 的概率。

但是出现了问题。

设考虑一个儿子 \(h\) 时,你枚举 \(h\) 的链长 \(j\),再枚举一个 \(f_i\),但它是由很多个不同 \(j\) 转移来的。

这个很麻烦,也就是说需要一个不含 \(h\) 贡献的 \(f\),首先想的是拼起来,但是这个复杂度包错的,出个菊花就卡了。

然后想到夜跑,没错又是夜跑,神题来了,可撤销树背包,我忘了夜跑是怎样的了,所以重新想。

发现困难,这个转移并非 01背包式的往后推一下,而是一个二维的东西。

设 \(h\) 最小可能长度为 \(len\) ,设扣除后的 \(f\) 为 \(g\),那么 \(g_{len}\) 显然随便求了,然后把 \(g_{len}\) 更新的东西在 \(f\) 里减去。

这样 \(len + 1\) 也没有收到任何影响,\(g_{len + 1}\) 也可求,往后退就全有了。

过了之后看题解发现这个转移其实是多项式乘法,我的撤销其实是多项式除法,确实除法,跟高精除相似。

有了 \(g\) 以后直接计算 \(h\) 是重儿子的概率,不是重儿子就是轻儿子,所以答案肯定好求。

这个做法只有 \(64\) 分,我还是优化掉逆元的log才交的,实在没招了给GPT了,GPT非要说我没问题,让我特判有 \(0\) 就不乘。

判完过了?!非常神秘,这个要记住真的太有用了。

P15650 [省选联考 2026] 摩卡串

看到这个题我想用Trie刻画一下,发现要么是 \(s\) 的左链要么是 \(s\) 真前缀才能字典序更小。

这个没啥用,然后考虑了dp,设 \(dp_{i,j}\) 表示前 \(i\) 位有 \(j\) 个字串字典序小于 \(s\),怎么转移?参考扫描线。

发现还要存当前和 \(s\) 的匹配情况,即有哪些后缀是 \(s\) 的前缀,这个我只想到了状压于是还是 \(15\) 分,等同于赛时得分。

题解的dp思路类似,也是类数位dp。

首先说一个性质,就是假设 \(s\) 这位是 \(1\),你填 \(0\),他就变成左链,以后填啥都比 \(s\) 小,设这样的位置有 \(c\) 个,那么没填上一位都会有 \(c\) 的贡献。

设 \(dp_{p , c , cnt , fl}\) 表示当前匹配到 \(p\) 位,\(c\) 个左链,总共 \(cnt\) 个字典序小于 \(s\),出没出现过 \(s\)。

发现这个是匹配过程,会有失配,所以考虑kmp优化dp,设 \(to_{p,0/1}\) 表示 \(p\) 位后面填 \(0/1\) 会跳到哪里。

设 \(num_p\) 表示 \(p\) 往前跳有多少个填 \(0\) 能获得左链的位置,这个看似很难,但是考虑kmp的性质,左链一定是有一段匹配上的,只有根匹配上也算。

那么其实就是跳fail,跳完还能匹配上,我们看当前的 \(j + 1\) 位是否为 \(1\) 即可,注意根节点要判。

设 \(dep_p\) 为 \(p\) 跳fail次数,用这个维护 \(s\) 的前缀,\(p == n\) 需要 \(-1\) 因为不是前缀,题目要求严格小于。

这两个东西都可以kmp维护。

转移部分如下:

  • \(p' = to_{p,i}\)
  • \(c' = c + (i == 0) \times num_p\)
  • \(cnt' = cnt + c'+ dep_{p'}\)
  • \(fl' = fl | (p' == n)\)

这个很简单,但是我们发现并没有记录长度,那怎么办呢?

考虑常规数位 \(dp\),用的是dfs,一般为计数,这个题是最优化的数位dp,每填一个数花费 \(1\) 跳到下一个状态,求到一个合法状态的最小花费。

这个显然可以抽象成最短路模型,因为边权均为 \(1\),甚至可以直接上bfs,实现 \(O(1)\) 量级的转移因为一个点就两条出边。

这个就能做了,下一个问题是复杂度,这个一看像 \(nk^2\),绝对不可能通过。

但是看 \(c\) 的转移,这个你不去填 \(0\),那么 \(c\) 一定很小,如果填了 \(0\),那么会产生巨多无比的贡献,是根号量级的,所以复杂度 \(O(nk \sqrt k)\),可以通过。

最后就是要输出方案,这个记录每个状态从哪转移来就行,同最短路输出路径。

自此做完了省选所有 < 黑的题。

posted @ 2026-10-21 15:36  wzc6666  阅读(4)  评论(0)    收藏  举报