日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

BZOJ 5326 [JSOI2017]博弈 (模拟费用流、线段树)

發布時間:2025/3/15 javascript 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BZOJ 5326 [JSOI2017]博弈 (模拟费用流、线段树) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接

https://www.lydsy.com/JudgeOnline/problem.php?id=5326

題解

終于成為第8個A掉這題的人……orz tzw神仙早我6小時
本以為這東西常數巨大,沒想到跑得還挺快,bzoj上不到5s就過了。

神仙題。
首先第一步轉化就相當神仙: 把數組按后手的優先級從高到低定序,原題的條件等價于先手要選出一些數,使得對于每個長度為\(i\)的前綴,選出的數都不超過\(\lceil \frac{x}{2}\rceil\)個。
然后顯然可以認為一開始的收益是\(-\sum^{n}_{i=1} b_i\), 每選一個\(i\)會得到\(a_i+b_i\)的收益,最大化總收益。

后面有三種理解方式:

(1) 直接用貪心理解。易證這個貪心滿足擬陣,直接按\(a+b\)從大到小排序,能選就選即可。

(2) 模擬費用流。從源點往每個點\(i (1\le i\le n)\)\((1,a_i+b_i)\), 從每個點\(i\)\(i+1\)\((\lceil \frac{i}{2}\rceil,0)\),匯點即為\(n+1\)號點。

(3) 另一種模擬費用流,轉化成老鼠進洞模型??紤]在\(1,3,5,7,9...\)位置各放一只老鼠,此外在每個位置上都視為有一個洞,老鼠只能往右走。
這樣就可以單次處理\(O(n\log n)\).
(我能說我理解這點東西就花了四個小時嗎……)

然后考慮如何支持修改。
下面以第二種方式理解,其余方式本質上完全相同。
一次增廣時我們就要找最長路,因為從\(i\)\(i+1\)的邊的代價都是\(0\), 所以就是要找所有滿足到匯點的邊都有剩余容量且未增廣的點中\(a_i+b_i\)最大的。
增廣之后,我們要把這條邊到匯點的流量全部\(-1\).
一次修改相當于先刪邊再加邊。
假設刪除的是從源點到點\(x\)的邊,若該邊目前已被增廣,那么我們要退回去,減去它的答案,同時所有后面的橫向邊容量\(+1\);否則直接刪除即可。
刪除之后我們最好保證答案依然最優,于是嘗試增廣(重復剛才提到的增廣一次的過程)。
添加的時候比較麻煩,因為如果增廣該邊的話可能需要替掉至多一條邊(莫名覺得很像最小生成樹,也許是擬陣的通性?)
所以首先判斷在不替掉任何邊的情況下是否可以直接增廣,如果可以的話直接增廣,否則我們找到一個點\(y\)滿足\(y\)\(x\)到匯點路徑上第一個\(0\)之前的\(a_i+b_i\)最小的點\(i\), 判斷用\(x\)替代\(y\)是否更優,更優則替代。
以上所有操作均可用線段樹維護(我寫了三棵線段樹,單點/區間修改、維護最大/最小值及其位置,三棵線段樹分別維護已增廣的點、未增廣的點和橫邊),時間復雜度\(O(n\log n)\).

代碼

因為我寫了三棵線段樹,所以碼長約6KB……

#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<utility> #include<cassert> #define llong long long #define pli pair<llong,int> #define pii pair<int,int> #define mkpr make_pair using namespace std;void read(int &x) {int f=1;x=0;char s=getchar();while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}x*=f; }const int N = 1e5; const llong INF = 10000000000ll; struct Element {llong a,b; int id;bool operator <(const Element &arg) const {return b>arg.b || (b==arg.b && id<arg.id);} } a[N+3]; llong c[N+3]; int w[N+3]; bool flow[N+3]; int id[N+3]; llong ans; int n,q;struct SegmentTree1 //maximum with no flow {pli sgt[(N<<2)+3];void update(pli &x,pli y) {if(y.first>x.first) {x = y;}}void pushup(int u){sgt[u] = mkpr(0,0);update(sgt[u],sgt[u<<1]);update(sgt[u],sgt[u<<1|1]);}void build(int u,int le,int ri,llong a[]){if(le==ri) {sgt[u] = mkpr(a[le],le); return;}int mid = (le+ri)>>1;build(u<<1,le,mid,a);build(u<<1|1,mid+1,ri,a);pushup(u);}void modify(int u,int le,int ri,int pos,llong x){if(le==ri) {sgt[u] = mkpr(x,pos); return;}int mid = (le+ri)>>1;if(pos<=mid) modify(u<<1,le,mid,pos,x);else modify(u<<1|1,mid+1,ri,pos,x);pushup(u);}pli querymax(int u,int le,int ri,int lb,int rb){if(le>=lb && ri<=rb) {return sgt[u];}int mid = (le+ri)>>1; pli ret = mkpr(0,0);if(lb<=mid) update(ret,querymax(u<<1,le,mid,lb,rb));if(rb>mid) update(ret,querymax(u<<1|1,mid+1,ri,lb,rb));return ret;} } sgt1;struct SegmentTree2 //minimum with flow {pli sgt[(N<<2)+3];void update(pli &x,pli y) {if(y.first<x.first) x = y;}void pushup(int u){sgt[u] = mkpr(INF,0);update(sgt[u],sgt[u<<1]);update(sgt[u],sgt[u<<1|1]);}void build(int n) {for(int i=0; i<=(n<<2); i++) sgt[i].first = INF;}void modify(int u,int le,int ri,int pos,llong x){if(le==ri) {sgt[u] = mkpr(x,pos); return;}int mid = (le+ri)>>1;if(pos<=mid) modify(u<<1,le,mid,pos,x);else modify(u<<1|1,mid+1,ri,pos,x);pushup(u);}pli querymin(int u,int le,int ri,int lb,int rb){if(le>=lb && ri<=rb) {return sgt[u];}int mid = (le+ri)>>1; pli ret = mkpr(INF,0);if(lb<=mid) update(ret,querymin(u<<1,le,mid,lb,rb));if(rb>mid) update(ret,querymin(u<<1|1,mid+1,ri,lb,rb));return ret;} } sgt2;struct SegmentTree3 //minimum flow {struct SgTNode{pii x; int tag;SgTNode() {x = mkpr(n,0); tag = 0;}} sgt[(N<<2)+3];void update(pii &x,pii y){if(y.first<x.first) {x = y;}else if(y.first==x.first && y.second<x.second) {x = y;}}void pushdown(int u){int tag = sgt[u].tag;if(tag){sgt[u<<1].x.first+=tag; sgt[u<<1].tag+=tag;sgt[u<<1|1].x.first+=tag; sgt[u<<1|1].tag+=tag;sgt[u].tag = 0;}}void pushup(int u){sgt[u].x = mkpr(INF,0);update(sgt[u].x,sgt[u<<1].x);update(sgt[u].x,sgt[u<<1|1].x);}void build(int u,int le,int ri,int a[]){if(le==ri) {sgt[u].x = mkpr(a[le],le); return;}int mid = (le+ri)>>1;build(u<<1,le,mid,a);build(u<<1|1,mid+1,ri,a);pushup(u);}void addval(int u,int le,int ri,int lb,int rb,int x){if(le>=lb && ri<=rb) {sgt[u].tag += x; sgt[u].x.first += x; return;}pushdown(u);int mid = (le+ri)>>1;if(lb<=mid) addval(u<<1,le,mid,lb,rb,x);if(rb>mid) addval(u<<1|1,mid+1,ri,lb,rb,x);pushup(u);}pii querymin(int u,int le,int ri,int lb,int rb){if(le>=lb && ri<=rb) {return sgt[u].x;}pushdown(u);int mid = (le+ri)>>1; pii ret = mkpr(n,0);if(lb<=mid) {update(ret,querymin(u<<1,le,mid,lb,rb));}if(rb>mid) {update(ret,querymin(u<<1|1,mid+1,ri,lb,rb));}pushup(u);return ret;}int query(int u,int le,int ri){if(sgt[u].x.first>0) return le;else if(le==ri) return le+1;pushdown(u);int mid = (le+ri)>>1,ret;if(sgt[u<<1|1].x.first>0) ret = query(u<<1,le,mid);else ret = query(u<<1|1,mid+1,ri);pushup(u);return ret;} } sgt3;void augment() {int pos = sgt3.query(1,1,n);if(pos<=n){pli aug = sgt1.querymax(1,1,n,pos,n);if(aug.first>0){ans += c[aug.second];sgt1.modify(1,1,n,aug.second,0);sgt2.modify(1,1,n,aug.second,c[aug.second]);sgt3.addval(1,1,n,aug.second,n,-1);flow[aug.second] = true;}} }int main() {scanf("%d",&n);for(int i=1; i<=n; i++) scanf("%lld",&a[i].a);for(int i=1; i<=n; i++) scanf("%lld",&a[i].b),a[i].id = i;sort(a+1,a+n+1);for(int i=1; i<=n; i++) id[a[i].id] = i;for(int i=1; i<=n; i++) c[i] = a[i].a+a[i].b;for(int i=1; i<=n; i++) ans -= a[i].b;for(int i=1; i<=n; i++) w[i] = (i+1)>>1;sgt1.build(1,1,n,c);sgt2.build(n);sgt3.build(1,1,n,w);for(int i=1; i<=((n+1)>>1); i++){augment();}printf("%lld\n",ans);scanf("%d",&q);while(q--){int u; llong x; scanf("%d%lld",&u,&x); u = id[u];if(flow[u]){ans -= c[u];sgt3.addval(1,1,n,u,n,1);sgt2.modify(1,1,n,u,INF);augment();flow[u] = false;}else{sgt1.modify(1,1,n,u,0);}c[u] = x+a[u].b;pii tmp = sgt3.querymin(1,1,n,u,n);if(tmp.first>0){ans += c[u];sgt2.modify(1,1,n,u,c[u]);sgt3.addval(1,1,n,u,n,-1);flow[u] = true;}else{pli tmp2 = sgt2.querymin(1,1,n,1,tmp.second);if(tmp2.first<INF && c[u]>c[tmp2.second]){sgt3.addval(1,1,n,tmp2.second,n,1);sgt3.addval(1,1,n,u,n,-1);sgt1.modify(1,1,n,tmp2.second,c[tmp2.second]);sgt2.modify(1,1,n,tmp2.second,INF);sgt2.modify(1,1,n,u,c[u]);ans -= c[tmp2.second];ans += c[u];flow[tmp2.second] = false;flow[u] = true;}else{sgt1.modify(1,1,n,u,c[u]);}}printf("%lld\n",ans);}return 0; }

總結

以上是生活随笔為你收集整理的BZOJ 5326 [JSOI2017]博弈 (模拟费用流、线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。