CSP2019&洛谷P5665:划分(单调队列,高精度)

解析

自己写的时候写了二维单调队列优化的64分
一次过还是可以满意了啦

正解的关键结论是最优的方案的最后一段一定尽可能的短
原因嘛…显然 贪心的想,再最后一段的段首可以往前放的情况下肯定是要往前放的,这样代价更小,同时对后面的选取也更加有利

这个性质是可以递归的
考虑如何求以i结尾的最后一段的最短长度
设以 i i i为末端的最短段的上一段的段尾在 p o s i pos_i posi​
设 s i s_i si​是 [ 1 , i ] [1,i] [1,i]的前缀和
那么 p o s i pos_i posi​就是满足:
s i − s j > = s j − s p o s j s_i-s_j>=s_j-s_{pos_j} si​−sj​>=sj​−sposj​​
的 j j j的最大值
移一下项:
s i > = 2 ∗ s j − s p o s j s_i>=2*s_j-s_{pos_j} si​>=2∗sj​−sposj​​
这个就可以用单调队列来维护了
时间复杂度 O ( n ) O(n) O(n)

关于最后的12分
需要高精
然而我上压位了还是T
qwq
int128 真香

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=4e7+100;
const double eps=1e-6;
const int mod=1333331;
inline ll read() {
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,m;

int pos[N];
ll sum[N];
int q[N],st,ed;
#define calc(o) (sum[o]*2-sum[pos[o]])
const int key=1e4;
struct bign{
	ll a[155],len;
};
inline bign trans(ll o){
	bign res;res.len=0;
	while(o){
		res.a[++res.len]=o%key;
		o/=key;
	}
	return res;
}
inline bign operator * (bign a,bign b){
	bign res;memset(res.a,0,sizeof(res.a));
	for(register int i=1;i<=a.len;i++){
		for(register int j=1;j<=b.len;j++) res.a[i+j-1]+=a.a[i]*b.a[j];
	}
	res.len=a.len+b.len;
	int p=0;
	for(register int i=1;i<=res.len;i++){
		res.a[i]+=p;
		p=res.a[i]/key;res.a[i]-=key*p;
		if(i==res.len&&p) res.len++;
	}
	while(!res.a[res.len]) res.len--;
	return res;
}
inline bign operator + (bign a,bign b){
	bign res;memset(res.a,0,sizeof(res.a));
	int top=a.len>b.len?a.len:b.len,p=0;
	for(register int i=1;i<=top;i++){
		res.a[i]+=a.a[i]+b.a[i]+p;
		p=res.a[i]/key;res.a[i]-=key*p;
		if(i==top&&p) top++;
	}
	res.len=top;
	return res;
}
__int128 res;
int b[N],p[N],l[N],r[N],w=(1<<30)-1;
void write(__int128 x){
	if(x>9) write(x/10);
	putchar('0'+x%10);
	return;
}
int main() {
	n=read();register int T=read();
	int a;
	if(T==0) for(register int i=1;i<=n;i++) a=read(),sum[i]=sum[i-1]+a;
	else{
		ll x=read(),y=read(),z=read();
		b[1]=read(),b[2]=read(),m=read();
		for(register int i=1;i<=m;i++){
			p[i]=read();l[i]=read();r[i]=read();
		}
		int pl=1;
		for(register int i=3;i<=n;i++) b[i]=(1ll*x*b[i-1]+1ll*y*b[i-2]+z)&w;
		for(register int i=1;i<=n;i++){
			if(i>p[pl]) ++pl;
			a=(b[i]%(r[pl]-l[pl]+1))+l[pl];sum[i]=sum[i-1]+a;
		}
	}
	for(register int i=1;i<=n;i++){
		while(st<ed&&sum[i]>=calc(q[st+1])) st++;
		pos[i]=q[st];
		while(st<=ed&&calc(q[ed])>=calc(i)) ed--;
		q[++ed]=i;
		//printf("i=%d pos=%d\n",i,pos[i]);
	}
	for(register int pl=n;pl;pl=pos[pl]){
		res=res+(__int128)(sum[pl]-sum[pos[pl]])*(sum[pl]-sum[pos[pl]]);
	}
	//printf("%d\n",res.len);
	write(res);
	return 0;
}
/*
100 1
123 456 789 12345 6789 3
2000000 123456789 987654321
7000000 234567891 876543219
10000000 456789123 567891234
*/
上一篇:性能分析----Perf+火焰(FlameGraph)图


下一篇:Oracle视图和PL SQL编程.