1. 程式人生 > >【CF878E】Numbers on the blackboard 並查集

【CF878E】Numbers on the blackboard 並查集

是什麽 first pan const gpo 最終 efi 是否 strong

【CF878E】Numbers on the blackboard

題意:給你一個長度為n個數列,你每次可以進行如下操作:

選取兩個相鄰的數x,y(x在y左面),然後將這兩個數去掉,用x+2y替換它。

重復此操作直到序列中只有一個數為止。你可以任意決定每次合並哪兩個數,求最後得到的數的最大值。

為了加大難度,現有q次詢問,每次詢問給出l,r,問你對[l,r]這段區間進行操作能得到的最大值是什麽。

n,q<=100000,ai<=10^9

題解:先不考慮l,r的限制,整個操作可以看成:讓你最大化$\sum a_i\times 2^{k_i},k_0=0,1<=k_i<=k_{i-1}+1$。我們從左往右逐個加入每個數,如果ai是負數,我們直接令$k_i=1$;否則我們令$k_i=k_{i-1}+1$。這樣的話最終得到的k一定是分為若幹段,每段(除了第一段)都是開頭的k=1,然後k不斷++。我們還需要判斷:在加入ai後,如果最後一段合並之後的和變成了正數,那麽還要將最後一段整體向前合並,直到和為負數為止。

如果考慮l,r呢?我們可以離線,對於r=i,我們用並查集找到l所在的塊,然後統計一下答案即可。

在判斷一個塊內合並後總和是否是正數時要討論一下。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <utility>
using namespace std;
#define mp(A,B) make_pair(A,B)
typedef long long ll;
const ll P=1000000007;
const ll inv=500000004;
const int maxn=100010;
int n,m;
int f[maxn],pre[maxn];
ll v[maxn],s[maxn],sum[maxn],ans[maxn],pw[maxn],sp[maxn];
vector<pair<int,int> > q[maxn];
vector<pair<int,int> >::iterator it;
int find(int x)
{
	return (f[x]==x)?x:(f[x]=find(f[x]));
}
inline void merge(int a,int b)
{
	if((a-pre[a]>31&&sum[b]>0)||sum[a]+(sum[b]<<(a-pre[a]))>P)	sum[b]=P;
	else	sum[b]=sum[a]+(sum[b]<<(a-pre[a]));
	f[a]=f[b],pre[b]=pre[a];
}
inline ll query(int a,int b)
{
	return (s[a]-s[b+1]*pw[b-a+1]%P+P)%P;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i,a,b;
	for(pw[0]=i=1;i<=n;i++)	f[i]=i,pre[i]=i-1,v[i]=rd(),pw[i]=(pw[i-1]<<1)%P;
	for(i=n;i>=1;i--)	s[i]=((s[i+1]<<1)+v[i]+P)%P;
	for(i=1;i<=m;i++)	a=rd(),b=rd(),q[b].push_back(mp(a,i));
	for(i=1;i<=n;i++)
	{
		sum[i]=v[i];
		while(pre[i]&&sum[i]>=0)	merge(pre[i],i);
		sp[i]=(sp[pre[i]]+(query(pre[i]+1,i)<<1))%P;
		for(it=q[i].begin();it!=q[i].end();it++)
		{
			a=(*it).first,b=find(a);
			ans[(*it).second]=(sp[i]-sp[b]+query(a,b)+P)%P;
		}
	}
	for(i=1;i<=m;i++)	printf("%I64d\n",ans[i]);
	return 0;
}

【CF878E】Numbers on the blackboard 並查集