#树状数组,倍增#洛谷 6859 蝴蝶与花

题目


分析

考虑以第一个开头的前缀如果存在那么一定存在长度为(s)(s+1)的路径和
那么要想消掉多去的1就得找到一个更短的连续2然后减去最近的1,这可以用树状数组单log维护


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=2000011;
int n,m,c[N],sum,a[N],two[21];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline void update(int x,int y){for (;x<=n;x+=-x&x) c[x]+=y;}
inline signed query(int x){
	rr int ans=0;
	for (;x;x-=-x&x) ans+=c[x];
	return ans;
}
inline signed answ(int s){
	rr int ans=0,now=0;
	for (rr int i=20;~i;--i)
	if (ans+two[i]<=n&&now+c[ans+two[i]]<s)
	    ans+=two[i],now+=c[ans];
	return ans+1;
}
inline signed get_two(int x){
	rr int now=0,ans=0,t=query(x-1);
	for (rr int i=20;~i;--i)
	if (ans+two[i]<x||(ans+two[i]<=n&&now+c[ans+two[i]]-t==(ans+two[i]-x+1)*2))
	    ans+=two[i],now+=c[ans];
	return ans;
}
signed main(){
	n=iut(),m=iut(),two[0]=1;
	for (rr int i=1;i<21;++i) two[i]=two[i-1]<<1;
	for (rr int i=1;i<=n;++i) a[i]=iut(),c[i]=c[i-1]+a[i];
	sum=c[n]; for (rr int i=n;i;--i) c[i]-=c[i&(i-1)];
	for (rr int i=1;i<=m;++i){
		rr char c=getchar();
		while (!isalpha(c)) c=getchar();
		if (c=='C'){
			rr int x=iut(),y=iut();
			update(x,y-a[x]),
			sum+=y-a[x],a[x]=y;
		}else{
			rr int s=iut(),t,now;
			if (!s||s>sum) {printf("none
"); continue;}
			t=answ(s),now=query(t);
			if (now==s) {print(1),putchar(32),print(t),putchar(10); continue;}
			rr int cnt1=get_two(1),cnt2=get_two(t)-t+1;
			if (cnt1>=cnt2){
				if (t+cnt2>n) printf("none
");
				    else print(1+cnt2),putchar(32),print(t+cnt2),putchar(10); 
			}else{
				if (t+cnt1>n) printf("none
");
				    else print(2+cnt1),putchar(32),print(t+cnt1),putchar(10); 				
			}
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13922304.html