2022 牛客多校题解
Contest 1
J
Contest 2
J
Contest 3
H Hack(SAM)
枚举B中的右端点,查询最长在A串中向右可以延伸多长。考虑对A串建立一个SAM,然后对于B串从左到右跑SAM的DAG,如果fail了就跳fa,将clen=Len[fa[now]]。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
long long pre[maxn],seg[maxn];
char a[maxn],temp[maxn];
int n,m,k;
void add(int p,long long val){
while(p>0)
seg[p]=max(val,seg[p]),p-=p&-p;
}
long long segque(int p){
long long ret=-1e18;
while(p<=m)
ret=max(ret,seg[p]),p+=p&-p;
return ret;
}
int trans[26][maxn],fa[maxn],Len[maxn],cnt=1,last=1;
void extend(int c){
int u=++cnt,v=last;
Len[u]=Len[v]+1;
while(v&&!trans[c][v]) trans[c][v]=u,v=fa[v];
if(!v) fa[u]=1;
else{
int x=trans[c][v];
if(Len[x]==Len[v]+1) fa[u]=x;
else{
int y=++cnt;
for(int t=0;t<26;++t)
trans[t][y]=trans[t][x];
fa[y]=fa[x]; fa[x]=fa[u]=y; Len[y]=Len[v]+1;
while(v&&trans[c][v]==x) trans[c][v]=y,v=fa[v];
}
}
last=u;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
scanf("%s",a+1);
for(int t=1;t<=n;++t)
extend(a[t]-'a');
for(int t=1;t<=m;++t)
scanf("%lld",pre+t),pre[t]+=pre[t-1];
for(int t=1;t<=k;++t){
scanf("%s",temp+1);
int now=1;
int clen=0;
for(int i=1;i<=m;++i) seg[i]=-1e17;
add(1,0);
long long ans=0;
for(int i=1;i<=m;++i){
while(fa[now]&&!trans[temp[i]-'a'][now])
clen=Len[fa[now]],now=fa[now];
if(trans[temp[i]-'a'][now])
now=trans[temp[i]-'a'][now],++clen;
if(clen){
if(clen<0)
cerr<<clen<<endl,exit(0);
ans=max(ans,segque(i-clen+1)+pre[i]);
}
add(i+1,-pre[i]);
}
//cerr<<endl;
printf("%lld\n",ans);
}
return 0;
}
D Directed (随机游走)
给定一棵树和一个起点,1号节点为终点,随机选其中K条边变成指向终点的单向边,在树上随机游走,求到达终点的期望步数
https://www.cnblogs.com/winlere/p/11852977.html
假设所有边都是无向边
设\(e_i\)表示从\(i\)点开始随机游走第一次到达的自己父亲的期望步数。根据这个链接的口胡,\({e_k}\)满足
答案是从s一直走父亲走到1
化简发现,一个子树内的边贡献为2,父边贡献为1,\(e_n=2 \text{siz[n]}-1\)
而单向边的影响是将自己祖先的siz[n]
减少,对期望的贡献为\(-2siz[u]\)。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int mod=998244353;
vector<int>e[maxn];
void add(int fr,int to){
e[fr].push_back(to);
e[to].push_back(fr);
}
int inv[maxn],jc[maxn],invi[maxn],fsum[maxn];
int MOD(const int&ba){return ba>=mod?ba-mod:ba;}
int MOD(const int&a,const int&b){return 1ll*a*b%mod;}
int ksm(const int&ba,const int&p){
int ret=1;
for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
if(t&1) ret=MOD(ret,b);
return ret;
}
void pre(const int&n){
jc[0]=inv[0]=1;
for(int t=1;t<=n;++t) jc[t]=MOD(jc[t-1],t);
inv[n]=ksm(jc[n],mod-2);
for(int t=n-1;t;--t) inv[t]=MOD(inv[t+1],t+1);
for(int t=1;t<=n;++t) invi[t]=MOD(jc[t-1],invi[t]);
}
int C(int n,int m){
if(n<m||m<0) return 0;
return MOD(MOD(jc[n],inv[m]),inv[n-m]);
}
int n,k,s;
int fa[maxn],siz[maxn],dep[maxn],path[maxn];
void dfs0(int x,int y){
fa[x]=y; siz[x]=1; dep[x]=dep[y]+1;
for(auto t:e[x])
if(t^y)
dfs0(t,x),siz[x]+=siz[t];
}
int sav;
void dfs1(int x,int d0){
//dep[x]-d0 ~ dep[x]-1
if(dep[x]>d0){
sav=(sav+2ll*siz[x]*(fsum[dep[x]-2]-fsum[dep[x]-d0-1]+mod) )%mod;
//cerr<<"qwwq"<<2ll*siz[x]*(fsum[dep[x]-2]-fsum[dep[x]-d0-1]+mod)%mod<<endl;
}
for(auto t:e[x])
if(fa[t]==x&&!path[t])
dfs1(t,d0);
}
int main(){
pre(1e6);
cin>>n>>k>>s;
for(int t=1,x,y;t<n;++t){
cin>>x>>y;
add(x,y);
}
if(k>0){
for(int t=1;n-1-t>=k-1;++t)
fsum[t]=C(n-1-t,k-1);
for(int t=1;t<=n;++t)
fsum[t]=MOD(fsum[t-1]+fsum[t]);
}
dfs0(1,0);
if(s==1) return puts("0"),0;
//if(k==0) return cout<<2*siz[s]-1<<endl,0;
int ans=0,x=s;
while(x!=1){
dfs1(x,dep[x]);
ans=(ans+C(n-1,k)*(2ll*siz[x]-1))%mod;
path[x]=1;
sav=(sav+fsum[dep[x]-2]*2ll*siz[x])%mod;
//cerr<<fsum[dep[x]-1]*2ll*siz[x]<<' '<<sav<<endl;
x=fa[x];
}
ans=1ll*(ans-sav+mod)*ksm(C(n-1,k),mod-2)%mod;
cout<<ans<<endl;
return 0;
}
Boss(模拟费用流)
需要把N个人派遣到K个城市,每个城市需要的人数是固定的。把不同的人派遣到不同城市,代价都是不同的,求最小代价。
考虑trival的费用流,图总共分为三层,S-N个人-K个城市-T。
考虑复杂度瓶颈在于SPFA的复杂度,主要是边太多了。但是我们考虑,直接维护\(O(K^2)\)种转移,也就是说,对于人当他第一次被加入到某个城市的时候,我们直接预处理出他\(O(K^2)\)种反边然后加入的到一个只有\(K\)个点的图。然后我们只需要在这个K个点的图上跑SPFA即可。并且用数据结构(set,priority_queue)维护住。
复杂度\(O(N\times \text{ spfa}(K^2)+NK^2 \log (NK))\),题解的复杂度分析好像错了。
然后我写的set,卡死了,后面换成懒惰堆就能过了。记住,set的erase操作特别慢!
#include<bits/stdc++.h>
using namespace std;
int qr(){
int ret=0,c=getchar();
while(!isdigit(c)) c=getchar();
while( isdigit(c)) ret=ret*10+c-48,c=getchar();
return ret;
}
const int maxn=1e5+5;
const int inf=1e9;
typedef priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> heap ;
heap e[11][11];
int cost[maxn][11],fl[11],N,K,d[11],last[11],in[11];
int belong[maxn];
typedef long long ll;
long long que(heap&e,int i){
while(e.size()&&belong[e.top().second]!=i)
e.pop();
if(e.empty()) return 1e15;
return e.top().first;
}
queue<int>q;
long long solve(){
for(int t=0;t<=K;++t) d[t]=inf;
for(int t=0;t<=K;++t) last[t]=0;
d[0]=0;
q.push(0);
while(q.size()){
int t=q.front();
q.pop(); in[t]=0;
for(int i=1;i<=K;++i)
if(i^t && que(e[t][i],t)+d[t]<d[i]){
d[i]=que(e[t][i],t)+d[t];
last[i]=t;
if(!in[i]) q.push(i),in[i]=1;
}
}
long long ret=inf; int i=0;
for(int t=1;t<=K;++t)
if(fl[t]&&d[t]<ret)
ret=d[t],i=t;
fl[i]--;
while(i){
int L=last[i];
int id=e[L][i].top().second;
belong[id]=i;
for(int t=1;t<=K;++t)
if(t!=i)
e[i][t].push({cost[id][t]-cost[id][i],id});
i=L;
}
return ret;
}
int main(){
N=qr(); K=qr();
for(int t=1;t<=K;++t)
fl[t]=qr();
for(int t=1;t<=N;++t)
for(int i=1;i<=K;++i){
cost[t][i]=qr();
e[0][i].push({cost[t][i],t});
}
long long ans=0;
for(int t0=N;t0;--t0)
ans+=solve();
cout<<ans<<endl;
return 0;
}
标签:11,const,int,题解,多校,long,牛客,maxn,ret
From: https://www.cnblogs.com/winlere/p/16747885.html