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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

模板:wqs二分

發(fā)布時間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 模板:wqs二分 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

所謂wqs,就是windwhisper說:“qs”

(逃)

解析

很神奇的科技。
四兩撥千斤的解決一些本來可能不太好解決的問題。

經(jīng)典模型:有若干個物品,要求選出 mmm 個,選的時候帶有限制,求最優(yōu)的方案。

這個正常 dp 常常需要加一維記錄個數(shù),復(fù)雜度 O(n2)O(n^2)O(n2) 難以通過。
設(shè) f(x)f(x)f(x) 表示選 xxx 個元素的最優(yōu)答案,那么 wqs 二分使用的前提是 f(x)f(x)f(x) 具有凸性
換句話說,所有的點 (i,f(i))(i,f(i))(i,f(i)) 共同形成一個凸包。

(以下以上凸包為例,下凸包同理)
考慮對這個凸包做一條斜率為 kkk 的切線,切于點 (x,f(x))(x,f(x))(x,f(x))
如何找到這條切線呢?
注意到,切線yyy 軸上的截距必然是最大的
設(shè)截距 D=f(x)?kxD=f(x)-kxD=f(x)?kx,我們就使要對于所有 xxx,求出 DDD 的最大值。
注意到 ?kx-kx?kx 這一項,那么我們只需要把每個物品的價值減去 kkk,然后直接求最大值即可。
如果這個切點不在我們需要的 mmm 處,由于這個切點橫坐標(biāo)是隨 kkk 單調(diào)的,所以我們可以二分尋找,直到可以切到 mmmkkk 為止。
找到正確的 kkk,求出對應(yīng)的 DDD 后,f(x)f(x)f(x) 就等于 D+kxD+kxD+kx,也就不難得到了。

一些細(xì)節(jié):

  • 切點很坐標(biāo)關(guān)于 kkk 的函數(shù)并不是連續(xù)的,因此可能需要最后一步強制選 mmm 個來算出答案,對應(yīng)的,我們也最好把恰好取 mmm 個的情況歸到大于 mmm 個的那邊。
  • 如果我們把相等的情況歸于大于,那么在物品權(quán)值相等的時候我們就需要把特殊物品優(yōu)先,這樣如果我們強制拋棄才不會出現(xiàn)選不夠 mmm 個的情況。
  • 例題

    給出一張圖,求出在滿足 111 的度數(shù)恰好為 mmm 的情況下的最小生成樹。

    顯然最小生成樹權(quán)值關(guān)于 111 的度數(shù)是一個凸函數(shù)。
    那么我們就二分一個 kkk,令所有和 111 相連的邊權(quán)值減去 kkk,跑一邊最小生成樹,看 111 的度數(shù)和 mmm 大小關(guān)系即可。

    #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") 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; } const int N=1e6+100; const int M=1e6+100; const int mod=1e9;int n,m,s,k;struct edge{int x,y,w,op,id; }e[N]; bool cmp(edge x,edge y){if(x.w!=y.w) return x.w<y.w;else return x.op>y.op; } int fa[N]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]); } ll ans; int q[N],num; int check(int val,int op=0){ans=0;//printf("\nval=%d op=%d\n",val,op);for(int i=1;i<=m;i++){e[i].op?e[i].w+=val:0;}for(int i=1;i<=n;i++) fa[i]=i;sort(e+1,e+1+m,cmp);int o=n,d=0;for(int i=1;o>1&&i<=m;i++){int x=find(e[i].x),y=find(e[i].y);//printf("(%d %d) val=%d\n",e[i].x,e[i].y,e[i].w);if(x==y) continue;if(op&&d==k&&e[i].op) continue;//printf(" ok\n");--o;d+=e[i].op;fa[x]=y;ans+=e[i].w;if(op) q[++num]=e[i].id; }ans-=d*val;if(o>1){printf("-1\n");exit(0);}for(int i=1;i<=m;i++){e[i].op?e[i].w-=val:0;}return d; }signed main(){#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);#endifn=read();m=read();s=1;k=read();for(int i=1;i<=m;i++){int x=read(),y=read(),w=read();e[i]=(edge){x,y,w,x==s||y==s,i};}int st=-4e4,ed=4e4;while(st<ed){int mid=(st+ed+1)>>1,num=check(mid);if(num>=k) st=mid;else ed=mid-1;//printf("mid=%d num=%d\n",mid,num);}if(st==-4e4) printf("-1\n");else{int nm=check(st,1);//printf("st=%d num=%d\n",st,num);printf("%d\n",num);for(int i=1;i<=num;i++) printf("%d ",q[i]);}return 0; } /* 5 6 1 3 1 2 2 1 4 5 1 5 5 1 3 2 3 5 4 2 4 4 */

    總結(jié)

    以上是生活随笔為你收集整理的模板:wqs二分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。