正睿停课集训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\)之后,\(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$的子树中有多少点可以选,转移直接树形背包
之后我们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\)被选择的次数,一个子树内的答案就是
最终将两个子树内的答案乘起来就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;
}