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;
}