"蔚来杯"2022牛客暑期多校训练营3
链接
\(A:Ancestor\)
预处理出 \(A,B\) 上关键点的前缀 \(lca\) 和后缀 \(lca\) ,枚举删除的点求即可。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
struct hh{
int to,nxt;
};
int n,k,nd[N];
IL LL in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
struct tree{
hh e[N<<1];int num,fir[N],val[N],dep[N],fa[N][20],lf[N],rf[N];
IL void add(int x,int y){
e[++num]=(hh){y,fir[x]},fir[x]=num;
}
void dfs(int u,int f){
dep[u]=dep[f]+1,fa[u][0]=f;
for(int i=0;fa[u][i];++i)
fa[u][i+1]=fa[fa[u][i]][i];
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
dfs(v,u);
}
IL int Lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;~i;--i)
if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=18;~i;--i)
if(fa[x][i]^fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
IL void init(){
lf[1]=nd[1];
for(int i=2;i<=k;++i)
lf[i]=Lca(lf[i-1],nd[i]);
rf[k]=nd[k];
for(int i=k-1;i;--i)
rf[i]=Lca(rf[i+1],nd[i]);
}
IL int get(int n){
if(n==1) return val[rf[2]];
if(n==k) return val[lf[k-1]];
return val[Lca(lf[n-1],rf[n+1])];
}
}A,B;
void solve(){
n=in(),k=in();int ans=0;
for(int i=1;i<=k;++i) nd[i]=in();
for(int i=1;i<=n;++i) A.val[i]=in();
for(int i=2;i<=n;++i) A.add(in(),i);
for(int i=1;i<=n;++i) B.val[i]=in();
for(int i=2;i<=n;++i) B.add(in(),i);
A.dfs(1,0),B.dfs(1,0),A.init(),B.init();
for(int i=1;i<=k;++i) ans+=(A.get(i)>B.get(i));
printf("%d\n",ans);
}
int main()
{
solve();
return 0;
}
\(C:Concatenation\)
对于字符串 \(a,b\),若 \(a+b<b+a\) ,则 \(a\) 排在 \(b\) 的前面,直接排序即可。
#include<bits/stdc++.h>
#define IL inline
using namespace std;
const int N=2e6+3;
string s[N];int n;
bool cmp2(const string &a,const string &b){
return a+b<b+a;
}
inline int in()
{
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
int main()
{
cin.tie(0)->sync_with_stdio(0);
cin.exceptions(cin.failbit);
cin.tie(NULL);
cout.tie(NULL);
cin>>n;
for(int i=1;i<=n;++i) cin>>s[i];
sort(s+1,s+n+1,cmp2);
for(int i=1;i<=n;++i) cout<<s[i];
return 0;
}
\(D:Directed\)
树上一个点到它父亲的期望步数为其子树大小乘 \(2\) 减 \(1\) (涨知识惹数学归纳法证?
令 \(1\) 为根,我们就是求 \(s\) 到 \(1\) 的路径上所有点到其父亲的期望步数之和。
先正常求出期望步数。
对于一条边变成单向边且满足从 \(s\) 一定能到 \(1\) ,显然只能变成内向边,相当于把某个子树删去。
我们对每条边,计算其如果被删去了对答案的贡献,即减去其子树大小乘 \(2\) 。
一条边的删去对路径上某个点到其父亲有贡献当且仅当这条边到那个点之间没有单向边,求出贡献的概率,这只与间隔的边数有关。因为一条边与路径上各个点相距的边数是连续的,所以求出概率的前缀和计算贡献即可 \(O(n)\) 解决。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e6+3,p=998244353;
struct hh{
int to,nxt;
}e[N<<1];
int n,k,s,num,fir[N],fac[N],ifac[N],bo[N],siz[N],dep[N],P[N],fa[N],bel[N],pp,ans;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
IL int ksm(int a,int b){
int c=1;
while(b){
if(b&1) c=1ll*c*a%p;
a=1ll*a*a%p,b>>=1;
}
return c;
}
void init(){
fac[0]=1;for(int i=1;i<=n;++i) fac[i]=1ll*fac[i-1]*i%p;
ifac[n]=ksm(fac[n],p-2);
for(int i=n;i;--i) ifac[i-1]=1ll*ifac[i]*i%p;
}
IL int C(int n,int m){if(n<m) return 0;return 1ll*fac[n]*ifac[m]%p*ifac[n-m]%p;}
void dfs1(int u,int f){
fa[u]=f,siz[u]=1,dep[u]=dep[f]+1;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^f) dfs1(v,u),siz[u]+=siz[v];
}
void dfs2(int u,int bl){
bel[u]=bl;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa[u]){
if(bo[v]) dfs2(v,v);
else dfs2(v,bl);
}
}
int main()
{
int x,y;
n=in(),k=in(),s=in(),init();
for(int i=1;i<n;++i)
x=in(),y=in(),add(x,y),add(y,x);
dfs1(1,0);
bo[s]=1;while(s!=1) bo[s=fa[s]]=1;
dfs2(1,1);
pp=ksm(C(n-1,k),p-2);
for(int i=1;i<n;++i) P[i]=1ll*C(n-1-i,k-1)*pp%p,P[i]=mod(P[i-1]+P[i]);
for(int i=2;i<=n;++i) if(bo[i]) ans=mod(ans+mod(2*siz[i]-1));
for(int i=2;i<=n;++i)
if(dep[i]>2){
if(bo[i]) ans=mod(ans-1ll*P[dep[i]-2]*mod(2*siz[i])%p+p);
else ans=mod(ans+p-1ll*mod(P[dep[i]-2]-P[dep[i]-dep[bel[i]]-1]+p)*mod(2*siz[i])%p);
}
printf("%d\n",ans);
return 0;
}
\(F:Fief\)
既然是取点就该想到点双。
如果全图就是一个点双,显然可行。
如果只有 \(2\) 个点,显然可行。
若全图有多个点双,如果二者都在同个点双,显然不可行。
枚举 \(1\) ~ \(n-1\) 的过程中点的变化是连续的,但如果有个人开始没占到某个割点,占到时肯定会把被该点分割的点集一并算在自己的范围内,不满足条件。
缩点后如果不是一条链,二人在度数为 \(3\) 的点双中争抢节点时,必然会产生上述情况。
需要图缩点后时一条链,且二者位于链两端的点集中且不为割点才满足条件。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
using namespace std;
const int N=2e5+3;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,q,num,col,tp,sta[N],fir[N],dfn[N],low[N],val[N];
vector<int>e1[N],e2[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void tarjan(int u){
dfn[u]=low[u]=++num,sta[++tp]=u;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(!dfn[v]){
tarjan(v),low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v]){
++col;
while(sta[tp+1]!=v)
e1[sta[tp]].pb(col),
e2[col].pb(sta[tp--]);
e1[u].pb(col),e2[col].pb(u);
}
}
else low[u]=min(low[u],dfn[v]);
}
IL void print(int op){for(int i=1;i<=q;++i) puts(op?"YES":"NO");exit(0);}
int main()
{
int x,y,z;
n=in(),m=in();
for(int i=1;i<=m;++i)
x=in(),y=in(),add(x,y),add(y,x);
q=in(),num=0,tarjan(1),num=0;
if(n==2) print(1);
for(int i=1;i<=n;++i)
if(!dfn[i]||e1[i].size()>2) print(0);
if(col==1) print(1);
for(int i=1;i<=col;++i){
int deg=0;
for(int j=0;j<e2[i].size();++j)
deg+=e1[e2[i][j]].size()-1;
if(deg>2) print(0);
if(deg==1){
++num;
for(int j=0;j<e2[i].size();++j)
if(e1[e2[i][j]].size()==1) val[e2[i][j]]=num;
}
}
while(q--) puts(val[in()]+val[in()]^3?"NO":"YES");
return 0;
}
\(H:Hacker\)
对模式串建立 \(SAM\) ,将匹配串的字符一个个走下去,没有该字符就向上跳 \(parent\) \(tree\) 上的父亲继续找,如此得到对于每个前缀 \(b_{1,i}\) 的可最长匹配的后缀,加个线段树维护权值前缀和的最小值即可。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
const LL inf=1e18;
struct node{
int ch[26],fa,len;
};
int n,m,k,ml,pos;
LL val[N],ans;
char s[N];
struct segment{
LL Min[N<<2];
#define ls k<<1
#define rs k<<1|1
IL void pushup(int k){Min[k]=min(Min[ls],Min[rs]);}
void build(int k,int l,int r){
if(l==r){Min[k]=val[l];return;}
int mid=l+r>>1;
build(ls,l,mid),build(rs,mid+1,r);
pushup(k);
}
LL query(int k,int l,int r,int ll,int rr){
if(l>=ll&&r<=rr) return Min[k];
int mid=l+r>>1;
if(ll>mid) return query(rs,mid+1,r,ll,rr);
if(rr<=mid) return query(ls,l,mid,ll,rr);
return min(query(ls,l,mid,ll,rr),query(rs,mid+1,r,ll,rr));
}
}T;
struct SAM{
node a[N];int las=1,cnt=1;
IL void ins(int pos,int c){
int p=las,np;a[np=las=++cnt].len=pos;
while(p&&!a[p].ch[c]) a[p].ch[c]=np,p=a[p].fa;
if(!p){a[np].fa=1;return;}
int q=a[p].ch[c];
if(a[q].len==a[p].len+1){a[np].fa=q;return;}
int nq=++cnt;a[nq]=a[q],a[nq].len=a[p].len+1;
a[np].fa=a[q].fa=nq;
while(p&&a[p].ch[c]==q) a[p].ch[c]=nq,p=a[p].fa;
}
void go(int wei,int c){
while(pos^1&&!a[pos].ch[c]) pos=a[pos].fa,ml=a[pos].len;
if(a[pos].ch[c]) pos=a[pos].ch[c],++ml;
ans=max(ans,val[wei]-T.query(1,0,m,wei-ml,wei));
}
}S;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
int main()
{
n=in(),m=in(),k=in();
scanf("%s",s+1);
for(int i=1;i<=n;++i) S.ins(i,s[i]-'a');
for(int i=1;i<=m;++i) val[i]=val[i-1]+in();
T.build(1,0,m);
while(k--){
scanf("%s",s+1),pos=1,ml=0,ans=0;
for(int i=1;i<=m;++i) S.go(i,s[i]-'a');
printf("%lld\n",ans);
}
return 0;
}
\(J:Journey\)
正常建图,用 \(deque\) \(bfs\) 即可,注意道路的表示,不要用两个路口而是用一个路口和四个方向表示。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+3;
struct hh{
int to,nxt,w;
}e[N<<4];
struct kk{
int x,y;
}S,T;
int n,c[N][4],dis[N][4];
IL LL in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
LL x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL kk turn(int x,int y){
int pos=0;
for(int i=1;i<4;++i)
if(c[x][i]==y) pos=i;
return (kk){x,pos};
}
deque<kk>q;
void bfs(){
q.push_front(S);dis[S.x][S.y]=0;
while(q.size()){
kk u=q.front();q.pop_front();
kk v=turn(c[u.x][u.y],u.x);
int op=(v.y+1)%4;
if(c[v.x][op]&&dis[v.x][op]>dis[u.x][u.y]){
dis[v.x][op]=dis[u.x][u.y];
q.push_front((kk){v.x,op});
}
for(int i=0;i<4;++i)
if(i^op&&c[v.x][i]&&dis[v.x][i]>dis[u.x][u.y]+1){
dis[v.x][i]=dis[u.x][u.y]+1;
q.push_back((kk){v.x,i});
}
}
if(dis[T.x][T.y]>1e9) printf("-1\n");
else printf("%d\n",dis[T.x][T.y]);
}
void solve(){
n=in();memset(dis,63,sizeof(dis));
for(int i=1;i<=n;++i)
for(int j=0;j<4;++j)
c[i][j]=in();
int x=in(),y=in();S=turn(x,y);
x=in(),y=in();T=turn(x,y);
bfs();
}
int main()
{
solve();
return 0;
}