首页 > 其他分享 >P6604 [HNOI2016] 序列 加强版

P6604 [HNOI2016] 序列 加强版

时间:2023-08-25 13:23:18浏览次数:44  
标签:P6604 int SUM pos HNOI2016 端点 100011 DP 加强版

链接:P6604 [HNOI2016] 序列 加强版
首先,像这种题可以转化为计算贡献,即计算每一个元素成为最小值的次数。

这个次数怎么求呢?显然单调栈模板,对于每一个数计算左边和右边第一个比它小的数\(l[i]\)和\(r[i]\)。

CODE 1:

for(int i=1;i<=n;i++){
	while(k && a[i]<a[sta[k]]){
		k--;
	}
	if(!k) l[i]=0;
	else{
		l[i]=sta[k];
	}
	sta[++k]=i;
}
k=0;
for(int i=n;i>=1;i--){
	while(k && a[i]<a[sta[k]]){
		k--;
	}
	if(!k) r[i]=n+1;
	else{
		r[i]=sta[k];
	}
	sta[++k]=i;
}

求出\(l[i]\)和\(r[i]\)后,我们发现虽然在线询问\([l,r]\),不好做,但是询问\([1,r]\)和\([l,n]\)是好做的,考虑先求出这两者,观察能否推广。

令\(dp[i]\)为以\(i\)结尾的所有字串的最小值之和,显然左端点在\(l[i]\)之前的部分最小值跟\(l[i]+1到i\)这一段毫无关系,所以这一部分的答案是\(dp[l[i]]\)。

而对于左端点在\(l[i]\)之后的部分,显然此时最小值是\(a[i]\)(第\(i\)的数的值),这一种情况有\(i-l[i]\)个区间,贡献是\(a[i]\times (i-l[i])\)

综上,\(dp[i]=dp[l[i]]+a[i]\times (i-l[i])\)

相同地,令\(DP[i]\)为从\(i\)开始的的所有字串的最小值之和,有\(DP[i]=DP[r[i]]+a[i]\times (r[i]-i)\)

令\(sum[i]\)为\([1,i]\)的答案,显然就是以\(1到i\)结尾的所有字符串的最小值之和,即\(sum[i]=\sum_{j=1}^{i} dp[j]\)

同样的,令\(SUM[i]\)为\([i,n]\)的答案,有\(SUM[i]=\sum_{j=i}^{n}DP[j]\)

CODE 2:

for(int i=1;i<=n;i++){
	dp[i]=dp[l[i]]+a[i]*(i-l[i]);
}
for(int i=n;i>=1;i--){
	DP[i]=DP[r[i]]+a[i]*(r[i]-i);
}
for(int i=1;i<=n;i++){
	sum[i]=sum[i-1]+dp[i];
}
for(int i=n;i>=1;i--){
	SUM[i]=SUM[i+1]+DP[i];
}

现在考虑怎么求出\([l,r]\)类型的答案。考虑分类贡献。我们使用\(st\)表预处理出\([l,r]\)区间内的最小值所在位置\(pos\)。

那么\([l,r]\)的所有子区间分为\(3\)种:

  • 1.左端点和右端点在\(pos\)两侧
  • 2.左端点和右端点在\(pos\)左侧
  • 3.左端点和右端点在\(pos\)右侧

第一种情况很简单,区间的个数是\((pos-l+1)\times (r-pos+1)\),每一个区间的最小值都是\(pos\),所以这部分的贡献是\((pos-l+1)\times (r-pos+1)\times a_{pos}\)

考虑第二种情况,你可能认为直接就是\(SUM[l]-SUM[pos]\),其实不然。因为这样只确定了左端点在\([l,pos)\)内而没有限制右端点。

考虑容斥,减去右端点在\(pos\)右侧的情况。此时左端点在\([l,pos)\)内,有\(pos-l\)种选择,对于每一种选择,既然\(pos\)是\([l,r]\)的最小位置,右端点落在\(pos\)右侧的贡献自然是\(DP[pos]\)。所以要减去\(DP[pos] \times (pos-l)\)

综上,第二种情况的贡献是\(SUM[l]-SUM[pos]-DP[pos]\times (pos-l)\)

同样的,第三种情况的贡献是\(sum[r]-sum[pos]-dp[pos]\times (r-pos)\)

把这三者加起来就是\([l,r]\)的贡献了。

下面是完整代码(ps:有的变量重名改了名)

CODE 3:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int sum[100011];
int SUM[100011];
int dp[100011];
int DP[100011];
int st[100011][21],pos[100011][21];
int n,q,type,sta[100011],k;
int a[100011];
int l[100011];
int r[100011];
unsigned long long re=0;

namespace gen{
	typedef unsigned long long ull;
	ull s,a,b,c,lastans=0;
	void in(){
		cin>>s>>a>>b>>c;
	}
	ull rand(){
		return s^=(a+b*lastans)%c;
	}
};

signed main(){
	cin>>n>>q>>type;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		st[i][0]=a[i];
		pos[i][0]=i;
	}
	for(int j=1;(1<<j)<=n;j++){
		for(int i=1;i+(1<<j)-1<=n;i++){
			if(st[i][j-1]<=st[i+(1<<j-1)][j-1]){
				st[i][j]=st[i][j-1];
				pos[i][j]=pos[i][j-1];
			}
			else{
				st[i][j]=st[i+(1<<j-1)][j-1];
				pos[i][j]=pos[i+(1<<j-1)][j-1];
			}
		}
	}
	for(int i=1;i<=n;i++){
		while(k && a[i]<a[sta[k]]){
			k--;
		}
		if(!k) l[i]=0;
		else{
			l[i]=sta[k];
		}
		sta[++k]=i;
	}
	k=0;
	for(int i=n;i>=1;i--){
		while(k && a[i]<a[sta[k]]){
			k--;
		}
		if(!k) r[i]=n+1;
		else{
			r[i]=sta[k];
		}
		sta[++k]=i;
	}
	for(int i=1;i<=n;i++){
		dp[i]=dp[l[i]]+a[i]*(i-l[i]);
	}
	for(int i=n;i>=1;i--){
		DP[i]=DP[r[i]]+a[i]*(r[i]-i);
	}
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+dp[i];
	}
	for(int i=n;i>=1;i--){
		SUM[i]=SUM[i+1]+DP[i];
	}
	if(type==1){
		gen::in();
	}
	int lt,rt;
	for(int i=1;i<=q;i++){
		if(!type){
			cin>>lt>>rt;
		}
		else{
			lt=gen::rand()%n+1;
			rt=gen::rand()%n+1;
			if(lt>rt) std::swap(lt,rt);
		}
		int poss;
		int kk=log2(rt-lt+1);
		if(a[pos[lt][kk]]<=a[pos[rt-(1<<kk)+1][kk]]) poss=pos[lt][kk];
		else poss=pos[rt-(1<<kk)+1][kk];
		unsigned long long ans=(unsigned long long)(a[poss]*(poss-lt+1)*(rt-poss+1));
		ans+=(unsigned long long)(sum[rt]-sum[poss]-dp[poss]*(rt-poss));
		ans+=(unsigned long long)(SUM[lt]-SUM[poss]-DP[poss]*(poss-lt));
		gen::lastans=ans;
		re^=ans;
	}
	cout<<re;
	return 0;
}

标签:P6604,int,SUM,pos,HNOI2016,端点,100011,DP,加强版
From: https://www.cnblogs.com/wangwenhan/p/17656605.html

相关文章

  • 例题两则(不无聊的子序列,HNOI2016序列)
    分享例题两则主要是分享一种\(\text{trick}\)。\(\text{UVA1608}\)题目描述给定一个长度为\(n\)的序列\(a\),如果\(a\)的每一个子串都存在至少一个元素只出现了一次,输出\(\text{Non-boring}\)。反之,输出\(\text{Boring}\)。\(n\leqslant2\times10^5\)。思路点......
  • 洛谷 U321190 麻将 加强加强版 题解
    Description给定一副\(k\)张牌的麻将牌,求能「听」哪些牌。对于所有数据,\(1\leqk\leq2\times10^5\)。link:https://www.luogu.com.cn/problem/U321190Solution算法零枚举「听」的牌,用状压DP或者贪心判断。时间复杂度\(\mathcal{O}(2^n\text{poly}(n))\)或\(\mathca......
  • 洛谷 P4931 [MtOI2018] 情侣?给我烧了!(加强版)
    洛谷传送门设\(f_i\)为\(i\)对情侣完全错位的方案数,那么答案为:\[\binom{n}{k}\frac{n!}{(n-k)!}2^kf_{n-k}\]分别代表选择\(k\)对情侣,选择它们的位置,情侣可以换位。\(f_i\)有递推公式:\[f_i=4i(i-1)(f_{i-1}+2(i-1)f_{i-2})\]考虑选出两个人,另外......
  • GLM 大加强,清华团队推出 GLM 联网加强版 WebGLM!
    夕小瑶科技说原创作者|小戏、ZenMoore大模型生成答案不可靠?一种很直接的思路就是结合传统的搜索引擎的“知识”来对大模型进行一次检索增强。其实早在InstructGPT面世以前,OpenAI就发布了可以用作搜索结果聚合的模型WebGPT,WebGPT基于GPT-3试图模仿人类的“搜索行为”......
  • DR5插件加强版 for Mac(ps磨皮滤镜) v5.0中文版
    dr5中文版是一款功能强大的ps磨皮插件集合版,整合了dr5磨皮美妆功能和工笔画功能,实现一个面板上集成众多修图功能,帮助用户一键磨皮降噪美白、局部平滑、表面平滑、改变色调、斑点祛除、液化校正、美白牙齿、眼睛增强等等,专注人像修饰,是您日常修图必备插件。DR5插件加强版下载Del......
  • 4543: [POI2014]Hotel加强版[树形DP+长链剖分]
    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4543 解题思路:长链剖分定义:f[i][j]表示以i为根节点的子树,有多少个节点和i的距离是j的.g[i][j]表示以i为根节点的子树,在子树外一个距离i为j的点可以跟i子树内的两个点组成两两相等的方案数.那么就有:f[u][j+1]+=f[v......
  • [BZOJ4407]于神之怒加强版 CODE
    #include<bits/stdc++.h>#definelllonglong#defineFor(i,a,b)for(lli=(a);i<=(b);++i)#defineRep(i,a,b)for(lli=(a);i>=(b);--i)constllN=1e6+10;usingnamespacestd;constllmod=1e9+7;llvis[N],tot,p[N];voidinit(lln){//质数筛For......
  • P8786 李白打酒加强版 题解
    李白打酒加强版题目大意一开始酒显里有\(2\)斗酒,每遇到一次店就会把酒显里的酒数量(单位:斗)乘\(2\),每遇到一次花就把酒显里的酒喝掉\(1\)斗。这一路上一共遇到店\(n\)次,遇到花\(m\)次,且最后一次遇到的是花,酒显里没有一斗酒了。计算这一路上遇到店和花的顺序总共有......
  • 最大子矩阵问题 加强版
    给定一个二维的数组(含正数或负数),请从中找出和最大的子矩阵。输入第一行:n,m接下来n行m列,表示一个二维数组输出 和为最大子矩阵的和样例样例输入440-2-7092-62-41-41-180-2样例输出15tips:#include<bits/stdc++.h>usingnamespacestd;l......
  • 统计方形(数据加强版)
    统计方形(数据加强版)题目背景1997年普及组第一题题目描述有一个\(n\timesm\)方格的棋盘,求其方格包含多少正方形、长方形(不包含正方形)。输入格式一行,两个正整数\(n,m\)(\(n\leq5000,m\leq5000\))。输出格式一行,两个正整数,分别表示方格包含多少正方形、长方形(不包含正......