正睿停课集训4

正睿停课集训4

思维不够发散,导致降智题目不会做

A

给定一棵树,每次可以移动到距离小于等于\(3\)的点上,求一个哈密顿回路

首先,一条链我们可以奇偶跳

一棵树,我们奇偶跳,最大距离不会超过三,所以降智题,直接根据深度奇偶分类

奇数递归跳,偶数回溯跳即可

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#include<assert.h>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 5e5 + 3;
struct edge{
	int to;
	int nxt;
}e[N << 1];
int head[N],deep[N],md[N],size[N];
int L[N],R[N],s[N],cnt;
int n,m,tot;
int ans[N],fa[N],t;
bool flag[N];
inline void add(int x,int y){
	e[++tot].to = y;
	e[tot].nxt = head[x];
	head[x] = tot;	
}
inline int read(){
	int v = 0,c = 1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') c = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}
inline void dfs(int x,int f,int dep){
	if(dep & 1) printf("%d ",x);
	for(int i = head[x];i;i = e[i].nxt){
		int y = e[i].to;
		if(y == f) continue;
		dfs(y,x,dep + 1);
	}	
if(!(dep & 1)) printf("%d ",x);
}
int main(){
puts("Yes");
	n = read();
	for(int i = 1;i < n;++i){
		int x = read(),y = read();
		add(x,y);
		add(y,x);	
	}
	dfs(1,0,1);
	return 0;
}

B

给定一个长度为的串\(S\)\(m\)个串\(T\),删掉第\(i\)个位置的代价为\(w_i\) 最小化使得\(S\)不包含任何\(T\)的代价(删掉之后两边不会拼到一起)

\(|T| \le |S| \le 2\times 10^5 ,m \le 10\)

首先,我们可以用kmp求出所有\(S\)\(T\)的匹配位置,之后考虑对于一个位置\(r\),若存在\(l_1,l_2(l_1\le l_2)\)使得\([l_1,r]\)\([l_2,r]\)\(S\)\(T\)的匹配位置,那么很明显\([l_1,r]\)这个限制是没有用的.

所以我们对于每一个位置,都只需要一个最右边的限制(如果存在的话),我们设为\(P_i\)

所以我们设\(f_i\)表示当前在\(i\)位置删除的最小代价,我们转移就有

\[f_{i} = \min f_j + a_i \]

这样转移有点问题,因为我们要保证满足上面区间区间的限制,我们发现,更新完\(f_i\)之后,\(P_i\)之前的状态就没有用了,因为如果从\(P_i\)之前更新过来会导致不满足\([P_i,i]\)这个限制

那么我们更新完\(f_i\)之后,就使得\(f_j = \infty,(j\in[0,P_i)\),这样就确保不会从不合法的地方转移

之后我们发现我们这个DP在区间查询最小值,将前缀赋值为\(\infty\),直接线段树优化即可

之后发现,区间赋值其实是单点赋值,因为每个位置只会被赋值为一次,而区间最小值我们可以改为查询后缀最小值,因为对于任意\(j >i\)有$f_j = \infty $

所以直接树状数组也可以

之后我们又发现,树状数组也不需要,可以直接上单调队列优化DP

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 3e5 + 3;
const int INF = 2e9 + 7;
char s[N];
int a[N],f[N];
char t[11][N];
int nxt[11][N];
int len[11];
int n,m;
vector <pii> B,G;
int Pi[N];
inline int read(){
	int v = 0,c = 1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') c = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}
inline void kmp(int x){
	nxt[x][1] = 0;
	int j = 0;
	for(int i = 2;i <= len[x];++i){
		while(j && t[x][j + 1] != t[x][i]) j = nxt[x][j];
		if(t[x][j + 1] == t[x][i]) j++;
		nxt[x][i] = j;	
	}
	j = 0;
	for(int i = 1;i <= n;++i){
		while(j && t[x][j + 1] != s[i]) j = nxt[x][j];
		if(t[x][j + 1] == s[i]) j++;
		if(j == len[x]){
			G.push_back(mk(i - len[x] + 1,i));
			j = nxt[x][j];
		}
	}
}
inline bool cmp(pii x,pii y){
	return x.se < y.se || (x.se == y.se && x.fi > y.fi);	
}
struct BIT{
	int c[N];
	inline void pre(){
		for(int i = 1;i <= n + 1;++i) c[i] = INF;
	}	
	inline void ins(int x,int v){
		c[x] = min(c[x],v);
		for(;x <= n + 1;x += x & (-x)) c[x] = min(c[x],v);
	}
	inline int query(int x){
		int res = INF;	
		for(;x;x -= x & (-x)) res = min(res,c[x]);
		return res;
	}
}T;
int main(){
	n = read();	m = read();
	scanf("%s",s + 1);
	for(int i = 1;i <= n;++i) a[i] = read();
	for(int i = 1;i <= m;++i){
		scanf("%s",t[i] + 1);
		len[i] = strlen(t[i] + 1);
		kmp(i);
	}
	sort(G.begin(),G.end(),cmp);
	for(int i = 0;i < G.size();++i){
		if(B.empty()){
			B.push_back(G[i]);
			Pi[G[i].se] = G[i].fi;
			continue;	
		}
		else{
			if(G[i].fi <= B.back().fi && G[i].se >= B.back().se) continue;
			else B.push_back(G[i]);
			Pi[G[i].se] = G[i].fi;
		} 
	}
//	for(int i = 1;i <= n;++i) printf("%d ",Pi[i]);puts("");	
	T.pre();
	f[0] = 0;T.ins(n + 1,0);
	int now = 0;int la = 0;
	for(int i = 1;i <= n;++i){
		f[i] = T.query(n - la + 1) + a[i];
//		printf("%d %d\n",i,T.query(n - la + 1));
		T.ins(n - i + 1,f[i]);
		la = max(la,Pi[i]);
		while(now < Pi[i]) f[now] = INF,now++;
	}
//	for(int i = 0;i <= n;++i) printf("%d ",f[i]);puts("");
	int ans = INF;
	for(int i = 0;i <= n;++i) ans = min(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}

C

给定一棵树,每个节点的度数不超\(L\),\(q\)次询问,每次询问形如\((u,v,k)\),表示有\(k\)个小球

给每一个小球指定一条路径,使得小球两两路径的交集恰好为\((u,v )\),由于球是往返运动,所以\((u,v)\)是无序数对,求方案数

两个方案不同,当且仅当有一个小球的路径不同(球区分,路径不区分)

首先,我们发现对于\((u,v)\),实质上让我们求\(u\)子树内的合法点和\(v\)子树内的合法点的个数

之后由于要求交集恰好为$(u,v) \(,所以在\)u\(的子树内选点必须满足两两之间LCA为\)u\(,换句话说,\)u\(子树内部每一棵子树内最多选择一个点,但是特殊地,\)u\(可以选择多次,这个最后直接用组合数解决即可,接下来想一下如何求\)u$的子树中有多少点可以选,转移直接树形背包

\[f_{i,j} = f_{i,j } + f_{i - 1,j - 1} \times size_k (k\in son_i) \]

之后我们DP出\(u,v\)对应的\(dp\)数组后

就可以算贡献,另外,如果一个点是lca,那么这个点不能从另外一个点的子树方向转移

时间复杂度是\(O(qL^2)\)的,遗憾不能通过本题

接下来考虑优化,发现大部分背包的状态都是可以利用的

由于背包具有交换律,所以这一我们引入一个新的做法:退背包

所谓的退背包,就是已经知道所有物品的一个DP背包,现在限制一个物品不能用,上面说到背包就有交换律,所以我们可以强制假设我们要退的这个物品是最后一个加入背包的,根据上面的方程式,我们很容易写出下面的代码

inline void ins(int x,int sz){
	for(int i = d[x] - 1;i >= 0;--i)
	dp[x][i + 1] = mo1(dp[x][i + 1] + 1ll * dp[x][i] * sz % mod);
}
inline void del(int x,int sz){
	for(int i = 0;i < d[x];++i){
		dp[x][i + 1] = mo2(dp[x][i + 1] - 1ll * dp[x][i] * sz % mod); 	
	}
}

注意枚举循序的差异,因为树形背包本质就是使用了滚动数组

这样对于一个\((u,v)\)我们只需要将黄色部分和蓝色部分退掉即可

最终答案我们就最后枚举\(u\)被选择的次数,一个子树内的答案就是

\[\sum_{i = 0}^k \binom{k}{i}\times dp_{u,k - i}\times (k -i)! \]

最终将两个子树内的答案乘起来就OK

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#include<assert.h>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 4e5 + 3;
const LL mod = 998244353;
struct edge{
	int to;
	int nxt;
}e[N << 1];
int head[N],size[N],deep[N];
int fac[N],inv[N];
int dp[N][505];
int fa[21][N],d[N];
int n,m,L,tot;
inline int read(){
	int v = 0,c = 1;char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') c = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}
inline LL quick(LL x,LL y){
	LL res = 1;
	while(y){
		if(y & 1) res = res * x % mod;
		y >>= 1;
		x = x * x % mod;
	}
	return res;
}
inline void add(int x,int y){
	e[++tot].to = y;
	e[tot].nxt = head[x];
	head[x] = tot;
	d[y]++;	
}
inline void dfs(int x,int f,int dep){
	deep[x] = dep;
	fa[0][x] = f;
	size[x] = 1;
	for(int i = head[x];i;i = e[i].nxt){
		int y = e[i].to;
		if(y == f) continue;
		dfs(y,x,dep + 1); 
		size[x] += size[y];
	} 	
}
inline int mo1(int x){
	if(x >= mod) x -= mod;
	 return x;	
}
inline int mo2(int x){
	if(x < 0) x += mod;
	return x;	
}
inline void ins(int x,int sz){
	for(int i = d[x] - 1;i >= 0;--i)
	dp[x][i + 1] = mo1(dp[x][i + 1] + 1ll * dp[x][i] * sz % mod);
}
inline void del(int x,int sz){
	for(int i = 0;i < d[x];++i){
		dp[x][i + 1] = mo2(dp[x][i + 1] - 1ll * dp[x][i] * sz % mod); 	
	}
}
inline int LCA(int x,int y){
	if(deep[x] < deep[y]) swap(x,y);
	for(int i = 19;i >= 0;--i)
		if(deep[fa[i][x]] >= deep[y]) x = fa[i][x];
	if(x == y) return x;
	for(int i = 19;i >= 0;--i)
		if(fa[i][x] != fa[i][y]) x = fa[i][x],y = fa[i][y];
	return fa[0][x];	
}
inline int kfa(int x,int k){
	for(int i = 19;i >= 0;--i) if(k & (1 << i)) x = fa[i][x];
	return x;	
}
inline int C(int n,int m){
	return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod; 	
}
bool flag = 0;
int main(){
//	freopen("C.in","r",stdin);
//	freopen("C.out","w",stdout);
	n = read(),m = read(),L = read();
	for(int i = 1;i < n;++i){
		int x = read(),y = read();
		add(x,y);
		add(y,x);	
	}
	fac[0] = inv[0] = 1;
	for(int i = 1;i <= L;++i) fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[L] = quick(fac[L],mod - 2);
	for(int i = L - 1;i >= 1;--i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
	dfs(1,0,1); 
	//for(int i = 1;i <= n;++i) printf("%d ",size[i]);puts("");
	for(int j = 1;j < 20;++j){
		for(int i = 1;i <= n;++i)
			fa[j][i] = fa[j - 1][fa[j - 1][i]];
	}
	for(int i = 1;i <= n;++i){
		dp[i][0] = 1; 
		for(int j = head[i];j;j = e[j].nxt){
			int y = e[j].to;
			int sz = (y == fa[0][i] ? n - size[i] : size[y]);
			ins(i,sz);
		}
	}
	while(m--){
		int x = read(),y = read(),k = read();
		int lca = LCA(x,y),sz1,sz2;
	//	printf("%d\n",lca); 
		if(deep[x] > deep[y]) swap(x,y);
		sz1 = (x == lca ? size[kfa(y,deep[y] - deep[x] - 1)] : n - size[x]);
		sz2 = n - size[y];
		del(x,sz1),del(y,sz2);
		int ans1 = 0,ans2 = 0;
		for(int i = 0;i <= k;++i){
			ans1 = mo1(ans1 + 1ll * C(k,i) * dp[x][k - i] % mod * fac[k - i] % mod);
			ans2 = mo1(ans2 + 1ll * C(k,i) * dp[y][k - i] % mod * fac[k - i] % mod);
		}
		printf("%lld\n",1ll * ans1 * ans2 % mod);
		ins(x,sz1),ins(y,sz2);
	}
	return 0;
}

posted @ 2019-10-22 20:19  wyxdrqcccc  阅读(162)  评论(0编辑  收藏  举报