• 【CF878E】Numbers on the blackboard 并查集


    【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 imes 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
    ",ans[i]);
    	return 0;
    }
  • 相关阅读:
    Javascript高级编程学习笔记(32)—— 客户端检测(1)能力检测
    Javascript高级编程学习笔记(31)—— BOM(5)screen、history对象
    Javascript高级编程学习笔记(30)—— BOM(4)navigator对象
    Javascript高级编程学习笔记(29)—— BOM(3)location对象
    Javascript高级编程学习笔记(28)—— BOM(2)window对象2
    Javascript高级编程学习笔记(27)—— BOM(1)window对象1
    逆向与反汇编工具
    Silicon Labs
    sk_buff 里的len, data_len, skb_headlen
    IP分片重组的分析和常见碎片攻击 v0.2
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8098246.html
Copyright © 2020-2023  润新知