codeforces 992E. Nastya and King-Shamans

樹狀數組 二分

題目傳送門

題目大意: 維護一個數列,每次操作爲先修改一個數,再詢問是否存在一個位置ii滿足w[i]=sum[i1]w[i]=sum[i-1]並輸出這個位置。

妙蛙

問題要求出滿足 Ax=sumx1A_x=sum_{x−1} 的位置,這個可以轉化爲 sumx=2sumx1sum_x=2sum_{x−1}

我們考慮從 Ap=1A_p=1 開始跳,每一次跳到其後面一個最小的 k1k−1 ,滿足sumk2sumpsum_k≥2sum_p

可以證明如果有答案且 sumans>0sum_{ans}>0,那麼答案一定在所有的 kk 之中產生

不妨用反證法來證明,假設當且跳到點 kk ,接下來選取的點是 k(k<k)k′ (k<k′),對於 k<i<k1k<i<k′−1

如果說 ii 是答案的話,設 yy 爲 第一個滿足 sumy2sumisum_y\geq2sum_i 的點。

因爲sumysumksum_y≥sum_k 所以必然有 yky≥k′ ,如果 i<k1i<k′−1 那麼 yi>1y−i>1 ,i 不是答案

所以證明了這樣跳,如果有答案的話答案必然在跳到的點上

所以可以用樹狀數組維護前綴和,每一次暴力二分跳,跳 loglog 次就能跳完,總複雜度是O(nlog3n)O(nlog^3n)

------Mangoyang

代碼:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
#define F inline
using namespace std;
typedef long long LL;
int n,q,a[N]; LL t[N];
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0; char ch=readc();
	while (!isdigit(ch)) ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x;
}
F void mdfy(int x,LL w){ for (;x<=n;x+=x&-x) t[x]+=w; }
F LL srch(int x){ LL s=0; for (;x;x-=x&-x) s+=t[x]; return s; }
F int pd(int x){
	int l=x+1,r=n,ans=x,mid; LL s=srch(x)<<1;
	while (l<=r)
		if (srch(mid=l+r>>1)<s) ans=mid,l=mid+1;
		else r=mid-1;
	return ans;
}
F int calc(){
	if (!t[1]) return 1;
	for (int x=1;x<n;x=max(x+1,pd(x)))
		if (srch(x+1)==srch(x)<<1) return x+1; 
	return -1;
}
int main(){
	n=_read(),q=_read();
	for (int i=1;i<=n;i++) a[i]=_read(),mdfy(i,a[i]);
	for (int p,x;q;q--)
		p=_read(),x=_read(),mdfy(p,x-a[p]),a[p]=x,printf("%d\n",calc());
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章