憨豆先生賣豆子(好題)(思維線段樹)(經典技巧)

2022-05-01 11:48:08 字數 1969 閱讀 5983

題意:給定n,p,k   n個數,然後取連續的一段,讓著一段的sum%p<=k,然後求滿足條件的最大sum/p

思路:見注釋

/*

取一段[l,r]讓這個區間和取模後小於等於k

切記,取模操作符合減法的分配定律

條件可以轉化為(sum-pre[l]-suf[r])%p<=k ===>(sum%p-pre[l]%p-suf[r]%p<=k

方法就是列舉這個pre[l]%p,然後可以藉此確定suf[r]%mod的最右邊的位置,更新最大值即可 */

#include

using

namespace

std;

typedef

long

long

ll;#define pb push_back

#define lson root<<1,l,midd

#define rson root<<1|1,midd+1,r

const

int m=1e6+6

;ll tree[m

<<2

],w[m],suf[m],pre[m],prel[m],sufr[m];

void up(int

root)

void build(int root,int l,int

r)

int midd=(l+r)>>1

; build(lson);

build(rson);

up(root);

}ll query(

int l,int r,int root,int l,int

r)

int midd=(l+r)>>1

; ll res=-2

;

if(l<=midd)

res=max(res,query(l,r,lson));

if(r>midd)

res=max(res,query(l,r,rson));

return

res;

}ll p;

ll mm(ll x)

intmain()

printf(

"case %d:

",sign);

ll summod=pre[n]%p;///

總的字首和取模

if(summod<=k)

suf[n+1]=0

;

for(int i=n;i>=1;i--)

build(

1,1,p);///

依照sufr來做線段樹,下標是字尾和取模,tree存的是位置

for(int i=1;i///

列舉從左開始的模p意義下的字首和

if(prel[i]==-1

)

continue

;

int temp=summod-i;///

總的減去左邊

int r=mm(temp);

int l=mm(temp-k);

if(l==0||l>r)///

查詢區間不合法

continue

;

int pos=query(l,r,1,1,p);//

返回滿足條件的最大值,即最後的座標

if(pos<=prel[i]+1)///

因為至少要取乙個袋子,所以如果pos==prel[i]+1,那麼利用下面步驟的處理就會選到0個袋子,所以不可取

continue

; ll x=pre[n]-pre[prel[i]]-suf[pos];

ans=max(ans,x/p);

}printf(

"%lld\n

",ans);

}return0;

}

view code

E Magic Stones (思維好題)

題意 給出a陣列,b陣列,下標為2 n 1 可以進行操作,a i a i 1 a i 1 a i 問進行若干次操作,能否將a陣列變成b陣列 思路 真是一道很巧妙的題目。對三個數字a i 1 a i a i 1,d1 a i a i 1 d2 a i 1 a i a i 可以這樣看,a i a i 1...

D Perfect Groups 思維好題

一道挺不錯的題目。定義乙個陣列的答案 將這個陣列分成k組,每組裡面任意一對數字乘積都是平方數,k要最小。題目給出乙個n個長度的大小,要你求每個子陣列的答案,最後輸出答案為1.2,3 n的子陣列各有多少個。n最大是5000,子陣列個數是n n 1 2,n平方也是沒問題的,因此我們只要考慮,如果o 1 ...

D Cleaning 思維好題 字首)

題意 n個數,現在你可以取出相鄰的都不為0的數,使得他們同時減1,如果乙個堆為0了,它並不會消失掉,此時我們有至多一次機會操作,交換相鄰兩個數的位置,問能不能使得全部的數都變為0.思路 首先發現,端點一定要 後面的值,不然肯定不成立。於是a2 a1,減了之後a2 a2 a1 此時a2變成端點,同理可...