绵阳东辰国际test201910.21

爆零警告,昨天晚上睡得晚,考试的时候困死我了

分析:

哈密尔顿环:每个点只经过一次的路线,有哈密尔顿环的图叫哈密尔顿图

先考虑只是一条链

很容易想到隔一个跳一次,这样无论链有多长一定是从A出发,B回来

所以直接拓展到树上即可:

如 果当前节点深度是奇数,那么我们在DFS前输出这个点,

否则在DFS完所有孩子之 后再输出这个点。

part code:

il void dfs(int u,int fa){
      dp[u]=dp[fa]+1;
      if(dp[u]&1){printf("%d\n",u);}
      for(ri i=head[u];i;i=edg[i].next){
      int to=edg[i].to;
      if(to==fa)continue;
      dfs(to,u);
      }
      if(!(dp[u]&1)){printf("%d\n",u);}
}

什么跳两次的滚出

总结:有很多的dfs题代码很简单,却很容易陷阱去,导致思路开花,应该往简单的方向想

不禁想到一句诗:众里寻他千百度,那人却在灯火阑珊处

分析:

首先用kmp或者哈希对每个 Ti,求出它在 S 中的匹配位置

那么这些位置 中至少得要删掉一个

考虑如果两个区间的R相等时,这时就只用管L最大的

证明

为什么因为先满足L较大的,其他的同R的肯定都满足了

而先满足L较小的,L较大的不一定满足,所以这样一定是最优的

于是对于每个 i,我们可以求出一个 Li

如果没有的直接从前面dp转移过来就可以

然后很明显就要dp

设dp[i]前i个位置都满足,且最后一个删的是i的最小值

Li是单调递增的

所以维护单调递增队列dp

code by CHiTongZi

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
char S[N], T[N];
int n, m, a[N], d[N];
inline void calc ()
{
	static int nxt[N];
	int cur = 0, len = strlen (T + 1);
	for (int i = 2; i <= len; ++i)
	{
		while (cur && T[cur + 1] != T[i]) cur = nxt[cur];
		nxt[i] = cur + (T[cur + 1] == T[i]);
		cur += T[cur + 1] == T[i];
	}

	cur = 0;
	for (int i = 1; i <= n; ++i)
	{
		while (cur && T[cur + 1] != S[i]) cur = nxt[cur];
		if (T[cur + 1] == S[i]) cur++;
		if (cur == len) d[i] = max (d[i], i - len + 1), cur = nxt[cur];
	}
}
int q[N], p[N], dp[N], tail = 1, head = 1;
inline void insert (int vv, int k)
{
	while (tail >= head && q[tail] >= k) tail--;
	q[++tail] = k;
	p[tail] = vv;
}
inline int query (int lp)
{
	while (tail >= head && p[head] < lp) head++;
	return q[head];
}
int main ()
{
	memset (d, -1, sizeof d);
	scanf ("%d%d", &n, &m);
	scanf ("%s", S + 1);
	for (int i = 1; i <= n; ++i)scanf ("%d", &a[i]);
	for (int i = 1; i <= m; ++i)scanf ("%s", T + 1), calc();
	int rpoint = -1;
	for (int i = 1; i <= n ; ++i)
	{
		if (rpoint >= d[i])d[i] = -1;
		rpoint = max (rpoint, d[i]);
	}
	for (int i = 1; i <= n; ++i)
	{
		insert (i, a[i] + dp[i - 1]);
		if (d[i] == -1) dp[i] = dp[i - 1];
		else dp[i] = query (d[i]);
	}
	printf ("%d\n", dp[n]);
	return 0;
}

分析:
问题转化:u 的子树中选 k 个点使它们两两 LCA 是 u 的方案数,对 v 也求同样的东西,

再把两者相乘就是最后的答案了

有可能u,v 存在祖孙关系,

不妨设 u 是 v 的祖先,那么 u 的子树就要改为以 v 的 方向作为根方向前提下的子树

然后就乱搞就行了

code:

#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
	int f=1;char k=getchar();x=0;
	for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
	for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
	x*=f;
}
template<class T>il _print(T x){
	if(x/10) _print(x/10);
	putchar(x%10+'0');
}
template<class T>il print(T x){
	if(x<0) putchar('-'),x=-x;
	_print(x);
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
	int res=1,bas=x%mod;
	while(m){
		if(m&1) res=(1ll*res*bas)%mod;
		bas=(1ll*bas*bas)%mod,m>>=1;
	}
	return res%mod;
}
const int MAXN = 1e5+5,mod = 998244353;
int n,m,L,u,v,k,head[MAXN],num_edge,dp[MAXN][505];
struct Edge{
	int next,to;
	Edge(){}
	Edge(int next,int to):next(next),to(to){}
}edge[MAXN<<1];
il add_edge(int u,int v){
	edge[++num_edge]=Edge(head[u],v),head[u]=num_edge;
	edge[++num_edge]=Edge(head[v],u),head[v]=num_edge;
}
it add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
it mul(int x,int y){return 1ll*x*y%mod;}
int f[MAXN][18],dep[MAXN],sz[MAXN],deg[MAXN];
il DFS(int u,int fa){
	sz[u]=1,f[u][0]=fa,dep[u]=dep[fa]+1;
	for(ri i=1;i<=17;++i) f[u][i]=f[f[u][i-1]][i-1];
	for(ri i=head[u];i;i=edge[i].next){
		if(edge[i].to==fa) continue;
		DFS(edge[i].to,u);
		sz[u]+=sz[edge[i].to];
	}
}
it LCA(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(ri i=17;i>=0;--i) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
	if(u==v) return u;
	for(ri i=17;i>=0;--i)
		if(f[u][i]!=f[v][i])
			u=f[u][i],v=f[v][i];
	return f[u][0];
}
it jump(int u,int lca){//找到u到lca这条路径上除了lca外深度最小的点
	for(ri i=17;i>=0;--i)
		if(dep[f[u][i]]>dep[lca])
			u=f[u][i];
	return u;
}
il calc(int u){//dp[u][i]表示在u的子树中选择其中i颗上的点的方案数。
	dp[u][0]=1;
	for(ri i=head[u];i;i=edge[i].next){
		if(edge[i].to==f[u][0]) continue;
		for(ri j=deg[u];j;--j)
			dp[u][j]=add(dp[u][j],mul(dp[u][j-1],sz[edge[i].to]));
	}
	for(ri i=deg[u];i;--i)
		dp[u][i]=add(dp[u][i],mul(dp[u][i-1],n-sz[u]));
}
int fac[MAXN],ifac[MAXN],inv[MAXN];
il init(){
	for(ri i=1;i<=n;++i) calc(i);
	fac[0]=ifac[0]=inv[1]=1;
	for(ri i=1;i<=L;++i) fac[i]=mul(fac[i-1],i);
	ifac[L]=qpow(fac[L],mod-2,mod);
	for(ri i=L-1;i;--i) ifac[i]=mul(ifac[i+1],i+1);
	for(ri i=2;i<=n;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
}
it C(int n,int m){return mul(fac[n],mul(ifac[n-m],ifac[m]));}
int ans[MAXN],tmp[MAXN];
it solve(int u,int k,int t){
	for(ri i=1;i<=deg[u];++i) ans[i]=0;//ans表示在背包中去掉大小为t的部分后的答案
	for(ri i=1;i<=deg[u];++i) tmp[i]=dp[u][i]; //tmp表示原来的答案
	//由于背包问题具有交换性(加入背包顺序可以改变),所以我们可以看做t是最后被加入背包的
	//因为我们有tmp[i]=ans[i]+ans[i-1]*sz,且在加入最后一个之前dp[deg[u]]=0,所以我们可以倒着推出ans[i]
	//即ans[i-1]=(tmp[i]-ans[i])/sz
	for(ri i=deg[u];i;--i){
		ans[i-1]=mul(tmp[i],inv[t]);
		tmp[i]=0,tmp[i-1]=add(tmp[i-1],mod-ans[i-1]);
	}
	ri res=0;
	for(ri i=0;i<=min(deg[u],k);++i) res=add(res,mul(mul(fac[i],ans[i]),C(k,i)));
	return res;
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n),read(m),read(L);
	for(ri i=1;i<n;++i) read(u),read(v),add_edge(u,v),++deg[u],++deg[v];
	DFS(1,0);
	init();
	for(ri i=1;i<=m;++i){
		read(u),read(v),read(k);
		if(dep[u]<dep[v]) swap(u,v);
		ri lca=LCA(u,v);
		if(lca!=v) print(mul(solve(u,k,n-sz[u]),solve(v,k,n-sz[v]))),puts("");
		else{
			ri t=jump(u,lca);
			print(mul(solve(u,k,n-sz[u]),solve(v,k,sz[t]))),puts("");
		}
	}
	return 0;
}
posted @ 2019-10-21 22:12  wzx_believer  阅读(172)  评论(0编辑  收藏  举报