[提高組集訓] 模擬賽6
阿新 • • 發佈:2021-11-17
風之軌跡「miracle」
題目描述
有 \(n\) 個點 \(m\) 條邊的有向無環圖,定義路徑長度為路徑上邊的數量。
問刪掉一個點之後所得到的最大的路徑長度,並且要求你輸出刪除的這個點(有多解輸出最小的一個)
\(n\leq 5\times 10^5,m\leq 10^6\)
解法
為了便於討論我們建立超級起點連向所有點,所有點再連向超級終點。
首先有一個基本的 \(\tt observation\):刪去 \(x\) 之後的最長路就是走起點拓撲序 \(<x\),終點拓撲序 \(>x\) 的邊得到的路徑最大值。
那麼我們可以預處理出經過每條邊的路徑最大值(正圖和反圖跑拓撲),然後類似掃描線,離開一個點的時候加入所有邊到 \(\tt set\)
總結
刪除一個點並不需要真的刪去,你可以轉化成強制不經過它。
#include <cstdio> #include <iostream> #include <queue> #include <set> using namespace std; const int M = 2000005; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,ans,id; struct node { int u,v,c; bool operator < (const node &b) const { if(c==b.c && u==b.u) return v<b.v; if(c==b.c) return u<b.u; return c>b.c; } };set<node> s; struct graph { int tot,cnt,f[M],dp[M],o[M],d[M]; struct edge{int v,next;}e[M<<1]; void add(int u,int v) { d[v]++; e[++tot]=edge{v,f[u]},f[u]=tot; } void tpsort() { queue<int> q; for(int i=0;i<=n+1;i++) if(!d[i]) q.push(i); while(!q.empty()) { int u=q.front();q.pop(); o[cnt++]=u; for(int i=f[u];i;i=e[i].next) { int v=e[i].v;d[v]--; dp[v]=max(dp[v],dp[u]+1); if(!d[v]) q.push(v); } } } }G1,G2; signed main() { freopen("miracle.in","r",stdin); freopen("miracle.out","w",stdout); n=read();m=read();ans=n; for(int i=1;i<=m;i++) { int u=read(),v=read(); G1.add(u,v); G2.add(v,u); } for(int i=1;i<=n;i++) { G1.add(0,i),G2.add(i,0); G1.add(i,n+1),G2.add(n+1,i); } G1.tpsort();G2.tpsort(); for(int i=0;i<=n+1;i++) { int x=G1.o[i]; for(int j=G2.f[x];j;j=G2.e[j].next) { int v=G2.e[j].v; s.erase(node{v,x,G2.dp[x]+G1.dp[v]+1}); } if(!s.empty()) { int c=s.begin()->c-2; if(c<ans || (c==ans && x<id)) ans=c,id=x; } for(int j=G1.f[x];j;j=G1.e[j].next) { int v=G1.e[j].v; s.insert(node{x,v,G2.dp[v]+G1.dp[x]+1}); } } printf("%d %d\n",id,ans); }
羽未「umi」
題目描述
給定一個長度為 \(n\) 的顏色序列 \(a_i\),我們稱序列是好的當且僅當所有顏色再序列上的出現位置連續。
我們可以把一種顏色整體修改成另一種顏色,問最終序列和原序列最小不同的位置數量。
你還需要支援 \(m\) 次修改,每次修改一個位置的顏色,然後給出答案。
\(n,m\leq 2\cdot 10^5\)
解法
考慮把原序列分段,那麼每一段的代價就是 長度\(-\)最多的出現次數。然後考慮分段方式是確定的,設 \(b_i\) 表示有多少 \([l_c,r_c]\) 覆蓋了 \([i,i+1]\),所有 \(b_i=0\) 的位置都是分界點。
問題變成了維護所有 \(0\)
我們可以轉而維護 \(b\) 陣列最小值之間的最大值之和,這樣區間加法就對它沒用影響了。上傳的時候需要先討論左右兒子最小值的關係,然後類似最大子段的方式合併即可,時間複雜度 \(O(n\log n)\)
#include <cstdio>
#include <set>
using namespace std;
const int M = 200005;
const int up = 200000;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,a[M],b[M],fl[4*M];set<int> s[M];
struct node
{
int mn,lm,rm,mx,sum;
node operator + (const node &b) const
{
node r;
if(mn>b.mn)
{
r.mn=b.mn;r.lm=max(mx,b.lm);
r.rm=b.rm;r.mx=max(mx,b.mx);
r.sum=b.sum;return r;
}
if(mn<b.mn)
{
r.mn=mn;r.lm=lm;
r.rm=max(rm,b.mx);r.mx=max(mx,b.mx);
r.sum=sum;return r;
}
r.mn=mn;r.lm=lm;r.rm=b.rm;
r.mx=max(mx,b.mx);
r.sum=sum+b.sum+max(rm,b.lm);
return r;
}
}t[4*M];
void fuck(int x,int c)
{
if(!x) return ;
t[x].mn+=c;fl[x]+=c;
}
void down(int i)
{
if(fl[i])
{
fuck(i<<1,fl[i]);
fuck(i<<1|1,fl[i]);
fl[i]=0;
}
}
void ins(int i,int l,int r,int id)
{
if(l==r)
{
t[i].lm=b[id];t[i].rm=b[id+1];
t[i].mx=max(b[id],b[id+1]);
return ;
}
int mid=(l+r)>>1;down(i);
if(mid>=id) ins(i<<1,l,mid,id);
else ins(i<<1|1,mid+1,r,id);
t[i]=t[i<<1]+t[i<<1|1];
}
void add(int i,int l,int r,int L,int R,int c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R) {fuck(i,c);return ;}
int mid=(l+r)>>1;down(i);
add(i<<1,l,mid,L,R,c);
add(i<<1|1,mid+1,r,L,R,c);
t[i]=t[i<<1]+t[i<<1|1];
}
void upd(int x)
{
if(!s[x].size()) return ;
int p=*s[x].begin();b[p]=s[x].size();
add(1,0,n,p,*s[x].rbegin()-1,1);
ins(1,0,n,p-1);ins(1,0,n,p);
}
void del(int x)
{
if(!s[x].size()) return ;
int p=*s[x].begin();b[p]=0;
add(1,0,n,p,*s[x].rbegin()-1,-1);
ins(1,0,n,p-1);ins(1,0,n,p);
}
signed main()
{
freopen("umi.in","r",stdin);
freopen("umi.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
s[a[i]].insert(i);
}
for(int i=1;i<=up;i++) upd(i);
printf("%d\n",n-t[1].sum);
while(m--)
{
int x=read(),y=read();
if(a[x]==y)
{
printf("%d\n",n-t[1].sum);
continue;
}
del(a[x]);del(y);
s[a[x]].erase(x);
s[y].insert(x);
upd(a[x]);upd(y);a[x]=y;
printf("%d\n",n-t[1].sum);
}
}