1. 程式人生 > >2020牛客多校第八場K題

2020牛客多校第八場K題

# __int128(例題:2020牛客多校第八場K題) ## 題意: > 有n道菜,第i道菜的利潤為$a_i$,且有$b_i$盤。你要按照下列要求給顧客上菜。 >1.每位顧客至少有一道菜 > 2.給顧客上菜時,都必須從第一道菜開始,上連續的編號的菜,例如,你可能給一位顧客 上的菜為第一道,第二道,第三道,但是不能為只上第二道而不上第一道,或者第一道,第三道中間缺少第二道。 > > 求這些菜能夠容納的最大顧客數,並且求出在容納最多顧客時的利潤最大為多少。 ##輸入輸出 >有t組樣例,沒組樣例第一行為$n$,表示有n道菜,接下來共有兩行,第一行n個數$a_1,a_2 ... a_n$,第i個數表示第i種菜的利潤,第二行為n個數$b_1,b_2...b_n$,第i個數表示第i種菜的個數。 ## 資料範圍 > $1\le n\le 10^{5}$,$-10^{9}\le a_i\le10^{9}$​,$1\le b_i\le10^{5}$ ## 輸入 ``` 2 3 2 -1 3 3 2 1 4 3 -2 3 -1 4 2 1 2 ``` ## 輸出 ``` Case #1: 3 8 Case #2: 4 13 ``` ## 說明 >對於測試案例1,最大訪問者數量為3,一種可能的解決方案是: >第一位訪客獲得食物1,利潤是2。 >第二位訪客獲得食物1,利潤為2。 >第三位訪客獲得食物1 +食物2 +食物3,利潤為2 +(-1)+ 3。 ## 題解 > 1.由於必須上第一道菜,因此第一道菜的數量即為最大招待顧客的數量。 > 2.計算最大利潤時,採用貪心策略,在滿足條件時,優先選取利潤最大的即可,由於每次選取 1~i的連續的菜,可以先求出前i份菜的利潤的字首和。然後按利潤字首和大小排序,從大到小遍歷,對於前n份菜利潤為sum的貢獻計算方法為,求出前n種菜剩餘的菜的最小的數量 min,讓答案累加$min*sum$,然後將前n種菜的數量都減去min,這個操作可以用線段樹來進行區間查詢和修改。 > 3.由於答案最大值為 $10^{19}$,稍微超過了long long int的範圍,因此c++要採用 __int128,__int128為128位的整數,是gcc的擴充套件,大小範圍在 [$2^{127},2^{127}-1$].且在windows環境下的gcc不支援編譯,在linux環境下的gcc支援編譯,不過沒有對應的輸入輸出函式,要自定義輸入輸出函式。 ## __int128模板 ```c++ `inline __int128 read(){//讀入 __int128 x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ ​ if(ch=='-') ​ f=-1; ​ ch=getchar(); } while(ch>='0'&&ch<='9') ​ x=x*10+ch-'0'; ​ ch=getchar(); } return x*f; } inline void fprint(__int128 x){//輸出。 if(x<0){ ​ putchar('-'); ​ x=-x; } if(x>9) ​ fprint(x/10); putchar(x%10+'0'); }` ``` ## 題目完整code ```c++ #include
#define scd(x) scanf("%d",&x) #define scdd(x,y) scanf("%d%d",&x,&y) #define sclld(x) scanf("%lld",&x) #define sclldd(x,y) scanf("%lld%lld",&x,&y) #define print(x) cout<inline tp lowbit(tp x){return x&(-x);} templatevoid debug(tp_ x) {cout<<"*********** "<void debug(tp1_ x,tp2_ y){cout<<"*********** "<elem2.x; return elem1.y'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*f; } inline void fprint(__int128 x){ if(x<0){ putchar('-'); x=-x; } if(x>9) fprint(x/10); putchar(x%10+'0'); } void build(int rt,int l,int r){ tree[rt].sum=tree[rt].lazy=0; if(l==r){ tree[rt].sum = b[l]; return ; } int mid = (l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); tree[rt].sum = min(tree[ls].sum,tree[rs].sum); } void pushdown(int rt,int l,int r){ if(tree[rt].lazy){ __int128 val = tree[rt].lazy; tree[ls].sum-=val; tree[rs].sum-=val; tree[ls].lazy += val; tree[rs].lazy += val; tree[rt].lazy = 0; } } void update(int rt,int l,int r,int L,int R,__int128 val){ if(l>=L&&r<=R){ tree[rt].sum -=val; tree[rt].lazy += val; return ; } pushdown(rt,l,r); int mid= (l+r)>>1; if(L<=mid)update(ls,l,mid,L,R,val); if(R>mid)update(rs,mid+1,r,L,R,val); tree[rt].sum = min(tree[ls].sum,tree[rs].sum); } __int128 ask(int rt,int l,int r,int L,int R){ if(l>=L&&r<=R){ return tree[rt].sum; } pushdown(rt,l,r); int mid = (l+r)>>1; __int128 res = inf; if(L<=mid)res=min(res,ask(ls,l,mid,L,R)); if(R>mid)res=min(res,ask(rs,mid+1,r,L,R)); return res; } void solve() { int n; scd(n); sum[0].y = 0; for(int i=1;i<=n;i++){ a[i]=read(); sum[i].x=i; sum[i].y=a[i]+sum[i-1].y; } for(int i=1;i<=n;i++)b[i]=read(); sort(sum+1,sum+n+1,cmp); __int128 visit = b[1]; build(1,1,n); __int128 ans = 0; for(int i=n;i>=1;i--){ __int128 cnt = ask(1,1,n,1,sum[i].x); ans+= cnt*sum[i].y; update(1,1,n,1,sum[i].x,cnt); } printf("Case #%d: ",Case); fprint(visit); printf(" "); fprint(ans); printf("\n"); } void Main(){ int _; scd(_); while(_--) solve(),++Case; } //#define _Debug int main(){ #ifdef _Debug freopen("data.in","r",stdin); clock_t b = clock(); Main(); clock_t e = clock(); cout<<"the time of cost is "<<(e-b)<