#loj3059 HNOI2019 序列

据说是保序回归问题?

业界都在用的算法是将序列分成若干段,然后每段取个平均值生成一个不降的b序列

至于为啥是对的?我也不知道啊

一开始想写个主席树

然后懒癌发作选择了反复二分的咸鱼做法

先考虑50分怎么做的,我们使用一个栈存储所有的段,

然后每次插入一个段的时候保证单调栈中每一段全部是单调递增的就ok了

提取一段的方差可以用平均数的平方减去平方的平均数来算

现在来考虑100分怎么搞

由于每次都是假如这个位置变成了xxx的修改,因此考虑将左侧前缀和右侧后缀的单调栈拼接在一起

将所有询问离线,然后假定更改的元素不会引起右侧单调栈的变化,然后尝试将这个点插入到左侧单调栈当中

使用二分而不是均摊的pop可以做到log的复杂度

此时我们得到了左侧的段,然后假定此时我们得到的左边界就是正确的,然后尝试将这一整段插入到右侧单调栈当中

借助二分法这一步的复杂度自然是log的

然后接着假定右侧的边界就是正确的边界,那么以此为依据去二分左侧

如此反复迭代若干次就可以得到正确的解了

局的这东西和单纯形算法很像,都是随机卡不掉但是构造就跪了的东西

但是事实上出题人为了卡掉仅仅二分一次的算法就竭尽全力的,所以迭代6,7次就能过了

#include<cstdio>
#include<algorithm>
#include<vector> 
using namespace std;const int N=1e5+10;typedef long long ll;
typedef long double ld;const ll mod=998244353;
ll inv[N];ll ansmid[N],ansleft[N],ansright[N];
int cur_round;
inline void pre()
{
	inv[0]=inv[1]=1;
	for(int i=2;i<N;i++)
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
struct data
{
	ll cnt0;ll cnt1;ll cnt2;
	friend data operator +(data a,data b)
	{
		return (data){a.cnt0+b.cnt0,a.cnt1+b.cnt1,(a.cnt2+b.cnt2)%mod};
	}
	friend data operator -(data a,data b)
	{
		return (data){a.cnt0-b.cnt0,a.cnt1-b.cnt1,(a.cnt2+mod-b.cnt2)%mod};
	}
	friend bool operator <(data a,data b)
	{
		if(cur_round)
			return (ld)(a.cnt1)/(a.cnt0)<(ld)(b.cnt1)/(b.cnt0);
		else 
			return (ld)(a.cnt1)/(a.cnt0)>(ld)(b.cnt1)/(b.cnt0); 
	}
	inline ll cval()
	{
		ll tval=cnt1%mod;(tval*=tval)%=mod;
		return (cnt2+tval*(mod-inv[cnt0]))%mod;
	}
}st[N],sum[N];ll ansst[N];int tp;int n;int m;
struct qry
{
	int id;data tmp;data ori;
};
vector <qry> vq[N];ll a[N]; 
inline void ins_ele(data tmp) 
{
	st[++tp]=tmp;
	while(tp>1&&st[tp]<st[tp-1])st[tp-1]=st[tp-1]+st[tp],tp--;
	sum[tp]=st[tp]+sum[tp-1];
	ansst[tp]=(st[tp].cval()+ansst[tp-1])%mod;
}
inline data fastpop(data qry,ll& mans,ll& mans2)
{
	int l=0;int r=tp;
	while(l!=r)
	{
		int mid=(l+r+1)>>1;
		if(st[mid]<(sum[tp]-sum[mid]+qry))l=mid;
		else r=mid-1;
	}
	
	
	mans2=(sum[tp]-sum[l]+qry).cval();
	mans=ansst[l];
	
	//printf("fast pop locte=%d %lld %lld\n",l,mans2,mans);
	return sum[tp]-sum[l];
}
int main()
{
	//freopen("mtst.in","r",stdin);
	pre();
	scanf("%d%d",&n,&m); 
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	cur_round=1;
	for(int i=1;i<=n;i++)
		ins_ele((data){1,a[i],(a[i]*a[i])%mod});
	printf("%lld\n",ansst[tp]);
	for(int i=1,u,k;i<=m;i++)
	{
		scanf("%d%d",&u,&k);
		data nw=(data){1,k,(ll)k*k%mod};
		vq[u].push_back((qry){i,nw,nw});
	}	
	for(int z=1;z<=20;z++)
	{
	//	printf("z=%d,front:\n",z);
		cur_round=1;
		tp=0;
		for(int i=1;i<=n;i++)
		{
			for(vector <qry> :: iterator it=vq[i].begin();it!=vq[i].end();++it)
					it->tmp=it->ori+fastpop(it->tmp,ansleft[it->id],ansmid[it->id]);
			ins_ele((data){1,a[i],a[i]*a[i]%mod});
		}
		cur_round=0;
	//	printf("z=%d,back:\n",z);
		tp=0;
		for(int i=n;i>=1;i--)
		{
			for(vector <qry> :: iterator it=vq[i].begin();it!=vq[i].end();++it)
					it->tmp=it->ori+fastpop(it->tmp,ansright[it->id],ansmid[it->id]);
			ins_ele((data){1,a[i],a[i]*a[i]%mod});
		}
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",(ansleft[i]+ansmid[i]+ansright[i])%mod);
	return 0;
}
posted @ 2019-05-05 17:22  sweetphoenix  阅读(171)  评论(0编辑  收藏  举报