P7516 [省选联考 2021 A/B 卷] 图函数
解析
純純的人類智慧題。
關鍵性質:vvv 可以在計算 f(u,G)f(u,G)f(u,G) 時產生貢獻,當且僅當 GGG 中 u,vu,vu,v 之間可以通過 [v,n][v,n][v,n] 的點互相到達。
充分性較為顯然,編號更大的點不會比 vvv 先刪去,所以必然在 vvv 時刻依然存在。
必要性證明:假設 vvv 時刻 u→vu\to vu→v 的路徑上存在一個 x∈[1,v)x\in [1,v)x∈[1,v),在 xxx 時刻圖的聯通性不弱于 vvv 時刻,因而必然也有 u→xu\to xu→x 可達,且由于 x→v,v→ux\to v,v\to ux→v,v→u 皆可達,也就有 x→ux\to ux→u 可達,xxx 應當被刪去,矛盾。
接下來的任務就是如何維護這個東西。
floyd
對于點編號屬于 [v,n][v,n][v,n] 這樣的限制,不難想到 floyd。但是直接跑無法通過 n=1000n=1000n=1000。
然后拿大樣例試了試,發現寫成:
答案依然是對的,并且能跑過1000了。
然后就完事了。
我們當然要想一想為什么這樣是對的。
這么轉移必然出問題的就是兩邊的點編號均大于 kkk 的情況。
如圖中的 p,qp,qp,q:
(高度表示點編號的相對大小)
那么我們現在就要證明:
若 (x,y)(x,y)(x,y) 路徑上沒有經過 [1,min?(x,y))[1,\min(x,y))[1,min(x,y)) 的點,就必然可以正確轉移。
假設枚舉到中轉點 kkk 之前的路徑都符合這個性質。
對于當前枚舉的中轉點 kkk,注意到我們需要求出的合法路徑必然至少有一個端點是小于 kkk 的。
那么就必然能在 kkk 的某一側找到第一個編號小于 kkk 的點 xxx,k→xk\to xk→x 這段路徑使用 (k,n](k,n](k,n] 的點中轉,不會出現現在 p,qp,qp,q 這樣的窘境,所以 f(k,x)f(k,x)f(k,x) 當前是對的。
那么 xxx 就可以通過 kkk 連接起另一側的 ppp ,得到正確的 f(p,x)f(p,x)f(p,x)。
如果 (u,p)(u,p)(u,p) 之間有 k′k'k′ 這樣的點導致 f(u,p)f(u,p)f(u,p) 不對怎么辦?把(u,p)(u,p)(u,p) 最小的點 k′k'k′ 當成 ppp 的角色來考慮,剛才的證明就還是對的。
bfs
本題看題解,還有通過 bfs 求對每個 vvv 考慮在每張圖中能對多少 uuu 產生貢獻的方法來做的,每次 bfs 用到一條邊就直接刪去,從而保證復雜度為 O(nm)O(nm)O(nm),也挺妙的。
代碼
#include<bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define debug(...) fprintf(stderr,__VA_ARGS__) #define ok debug("OK\n") using namespace std;const int N=1050; const int M=2e5+100; const int inf=1e9; const int mod=19921228;inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f; }int n,m; int f[N][N]; ll sum[M];signed main() { #ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout); #endif//memset(f,0x3f,sizeof(f));n=read();m=read();for(int i=1;i<=m;i++){int x=read(),y=read();f[x][y]=i;}for(int i=1;i<=n;i++) f[i][i]=m+1;for(int k=n;k>=1;k--){for(int i=k;i<=n;i++) sum[min(f[i][k],f[k][i])]++;for(int i=1;i<=k;i++){//if(!f[i][k]&&!f[k][i]) continue;for(int j=i;j<=n;j++){f[i][j]=max(f[i][j],min(f[i][k],f[k][j]));f[j][i]=max(f[j][i],min(f[j][k],f[k][i]));}}}for(int i=m;i>=1;i--) sum[i]+=sum[i+1];for(int i=1;i<=m+1;i++) printf("%lld ",sum[i]);return 0; } /* */總結
以上是生活随笔為你收集整理的P7516 [省选联考 2021 A/B 卷] 图函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 模板:拓展kmp(Z函数)
- 下一篇: P6620 [省选联考 2020 A 卷