爆零警告,昨天晚上睡得晚,考试的时候困死我了
分析:
哈密尔顿环:每个点只经过一次的路线,有哈密尔顿环的图叫哈密尔顿图
先考虑只是一条链
很容易想到隔一个跳一次,这样无论链有多长一定是从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; }