「九省联考 2018」制胡窜 (SAM)(线段树合并)(分类讨论)

L O J LOJ LOJ 传送门

题解:
好题啊,除的写起来伤心情……
首先转换一下题意,找到这个串的所有出现位置,这个可以用 S A M SAM SAM 加倍增实现,然后砍两刀使得每一个出现位置都被砍断,下面我们对这两刀怎么砍分类讨论:

一些规定: 令每个串的出现位置为 l i , r i l_i,r_i li,ri,出现的次数为 m m m,令 L L L r 1 r_1 r1 R R R l m l_m lm,一个点的(不严格可以取等)前驱为它前面第一个出现的串的 e n d p o s endpos endpos r i r_i ri,一个点的后继(可以取等)为后面第一个 r i r_i ri,符号分别为 p r e , s u f pre,suf pre,suf,本次询问的覆盖范围为 [ l 1 , r m ] [l_1,r_m] [l1,rm],本次询问串的长度为 l e n len len,在 i i i 切一刀指的是将 ( i , i + 1 ) (i,i+1) (i,i+1) 切断

  • C a s e   1 : Case\ 1: Case 1:
    R R R 的前驱的左端点与 S l 1 , r 1 S_{l_1,r_1} Sl1,r1 不相交,或者只交了一个字符,这种情况显然方案为 0

  • C a s e   2 : Case\ 2: Case 2:
    L > R L> R L>R,这种情况中可以分为几类:第一刀没切完第二刀把剩下的切完,第一刀切完,第二刀也切完或切第一刀左边的一部分或随便切一个地方( ∉ [ l 1 , r m ] \notin [l_1,r_m] /[l1,rm]
    发现这个贡献可以分段统计,枚举第一刀的端点 [ l i , l i + 1 ) [l_i,l_{i+1}) [li,li+1),那么可行的第二刀可以切在 [ R , r i + 1 ) [R,r_{i+1}) [R,ri+1)
    这个的方案数是 ∑ i ( l i + 1 − l i ) ( r i + 1 − R ) \sum_i (l_{i+1}-l_i)(r_{i+1}-R) i(li+1li)(ri+1R),第一刀切在中间,需要加上不同于刚刚那种情况的答案,分都在中间还是只有第一刀在中间讨论,那么可以得到方案数为 ( L − R 2 ) + ( L − R ) ( n − l e n ) \binom{L-R}{2}+(L-R)(n-len) (2LR)+(LR)(nlen)
    那么我们可以考虑用线段树维护 ∑ i ( l i + 1 − l i ) , ( r i + 1 ∑ i l i + 1 − l i ) \sum_i(l_{i+1}-l_i),(r_{i+1}\sum_i l_{i+1}-l_i) i(li+1li),(ri+1ili+1li)

  • C a s e   3 : Case\ 3: Case 3:
    L ≤ R L\le R LR,这种情况不存在一刀端,必须两刀配合完成,同样考虑当前选择 [ l i , l i + 1 ) [l_i,l_{i+1}) [li,li+1)
    那么需要满足 r i + 1 > R r_{i+1}>R ri+1>R,同时 l i + 1 < L l_{i+1}<L li+1<L ,于是我们可以在线段树上完成这样一个区间查询
    假设最后一个合法的区间为 k k k l k + 1 < L l_{k+1}<L lk+1<L),那么 [ l k + 1 , L ) [l_{k+1},L) [lk+1,L) 是可以切的,找到这个 k k k 可以通过查 L + l e n − 1 L+len-1 L+len1 的前驱和 L + l e n L+len L+len 后继来实现,贡献为 ( p r e − ( L + l e n − 1 ) ) ∗ ( s u f − R ) (pre-(L+len-1))*(suf-R) (pre(L+len1))(sufR)
    同时需要找到第一个满足 r i + 1 > R r_{i+1}>R ri+1>R 的区间最为起始节点,这个可以通过 R R R 的前缀找到 i i i

然后就做完啦,个人感觉讨论得还比较清楚


#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
typedef long long ll;
cs int N = 3e5 + 50;
cs int INF = 1e9;
int n, m, ps[N], rt[N]; ll ans[N];
char S[N];
struct query{ 
	int l, r, c; 
	query(int _l=0, int _r=0, int _c=0){ l=_l; r=_r; c=_c; }
};
vector<query> qry[N];
vector<int> G[N]; int fa[N][20];
namespace SGT{
	cs int N = ::N * 40;
	int nd,ls[N],rs[N],mi[N],mx[N]; ll v1[N],v2[N];
	#define mid ((l+r)>>1)
	void pushup(int x){
		if(!ls[x]){ mi[x]=mi[rs[x]]; mx[x]=mx[rs[x]]; v1[x]=v1[rs[x]]; v2[x]=v2[rs[x]]; return; }
		if(!rs[x]){ mi[x]=mi[ls[x]]; mx[x]=mx[ls[x]]; v1[x]=v1[ls[x]]; v2[x]=v2[ls[x]]; return; }
		mi[x]=mi[ls[x]]; mx[x]=mx[rs[x]];
		int delta = mi[rs[x]]-mx[ls[x]];
		v1[x]=v1[ls[x]]+v1[rs[x]]+(ll)delta*mi[rs[x]];
		v2[x]=v2[ls[x]]+v2[rs[x]]+delta;
	}
	void ins(int &x, int l, int r, int p){
		if(!x) x=++nd; mi[x]=mx[x]=p; if(l==r) return;
		(p<=mid)?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
	}
	void merge(int &x, int y){
		if(!x||!y){ x|=y; return; }
		merge(ls[x],ls[y]); merge(rs[x],rs[y]); pushup(x);
	}
	int qrymi(int x, int l, int r, int L, int R){
		if(!x) return INF;
		if(L<=l&&r<=R) return mi[x]; int as=INF;
		if(L<=mid) as=min(as,qrymi(ls[x],l,mid,L,R)); if(as!=INF) return as;
		if(R>mid) as=min(as,qrymi(rs[x],mid+1,r,L,R)); return as;
	}
	int qrymx(int x, int l, int r, int L, int R){
		if(!x) return -INF;
		if(L<=l&&r<=R) return mx[x]; int as=-INF;
		if(R>mid) as=max(as,qrymx(rs[x],mid+1,r,L,R)); if(as!=-INF) return as;
		if(L<=mid) as=max(as,qrymx(ls[x],l,mid,L,R)); return as;
	}
	ll as1, as2, trans;
	void query(int x, int l, int r, int L, int R){
		if(!x) return; 
		if(L<=l && r<=R){
			ll delta = mi[x] - trans; trans = mx[x];
			as1 += v1[x] + delta * mi[x];
			as2 += v2[x] + delta; return;
		}
		if(L<=mid) query(ls[x],l,mid,L,R);
		if(R>mid) query(rs[x],mid+1,r,L,R); 
	}
	ll qry(int x, int l, int r, int ps, int R){
		trans=ps; as1=as2=0; query(x,1,n,l,r); 
		return as1-as2*R;
	}
	ll calc(int x, int R){ return v1[x]-(ll)v2[x]*R; }
}
namespace SAM{
	int ch[N][10],lk[N],len[N],r[N],nd=1,las=1;
	int extend(int k, int c){
		int p=las, now=++nd; len[now]=len[las]+1; r[now]=k;
		for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;
		if(!p) lk[now] = 1;
		else{
			int q=ch[p][c];
			if(len[q]==len[p]+1) lk[now]=q;
			else{
				int cl=++nd; len[cl]=len[p]+1; lk[cl]=lk[q];
				memcpy(ch[cl],ch[q],sizeof(ch[q]));
				lk[q]=lk[now]=cl; 
				for(;p&&ch[p][c]==q;p=lk[p]) ch[p][c]=cl;
			}
		} las = now; return now;
	}
	void ready(){ 
		for(int i=1; i<=nd; i++) G[lk[i]].pb(i); 
		for(int i=1; i<=nd; i++) if(r[i]) SGT::ins(rt[i],1,n,r[i]);
	}
}
void pre_dfs(int u){
	for(int i=1; i<=18; i++)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int v : G[u]) fa[v][0]=u, pre_dfs(v);
}
int jump(int l, int r){
	int nx = ps[r], len = r-l+1;
	for(int i=18; ~i; i--) if(SAM::len[fa[nx][i]]>=len) nx=fa[nx][i];
	return nx;
}
ll C2(int x){ return (ll)x*(x-1)/2;}
void work(int u){
	for(int v : G[u]){
		work(v); 
		SGT::merge(rt[u],rt[v]);
	}
	int L = SGT::mi[rt[u]], ed = SGT::mx[rt[u]];
	for(auto t : qry[u]){
		int len = t.r - t.l + 1, R = ed - len + 1;
		if(R<=L) ans[t.c] = SGT::calc(rt[u],R) + C2(L-R) + (ll)(L-R)*(n-len);
		else{
			int pos = SGT::qrymx(rt[u],1,n,1,R);
			if(L+len-1<=pos) continue;
			ans[t.c] = SGT::qry(rt[u],R+1,L+len-1,pos,R);
			int rp = SGT::qrymi(rt[u],1,n,L+len,n), lp = SGT::qrymx(rt[u],1,n,1,L+len-1);
			if(rp!=INF&&lp!=-INF) if(rp>R) ans[t.c]+=(ll)(rp-R)*(L-(lp-len+1));
		}
	}
}
int main(){
	n = read(), m = read();
	scanf("%s",S+1); 
	for(int i=1; i<=n; i++) ps[i]=SAM::extend(i,S[i]-'0'); 
	SAM::ready(); pre_dfs(0);
	for(int i=1; i<=m; i++){
		int l=read(), r=read(), nx=jump(l,r);
		qry[nx].push_back(query(l,r,i));
	}
	work(0); ll sm = C2(n-1);
	for(int i=1; i<=m; i++) cout << sm-ans[i] << '\n';
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 黄河九地图数据Shp是一种包含九个份的地理信息系统文件格式,可以在各种GIS软件中使用。这些九个份包括青海、甘肃、宁夏、内蒙古、陕西、山西、河南、山东和河北。该地图数据Shp覆盖了全部黄河流域,以黄河为中心,包括了黄河流域内的山脉、河流、湖泊、城市、道路等地理要素。使用这个地图数据Shp可以提供各种黄河流域的地理信息分析与处理,如水文、水资源、水环境、地质灾害、城市规划、交通等领域。 该地图数据Shp由多个文件组成,其中包括.shp、.shx、.dbf等几个文件。其中.shp文件存储了地图数据几何图形信息,.shx文件存储了几何图形的索引信息,.dbf文件存储了属性数据信息。可以使用各种GIS软件打开这些文件,可以查看和编辑地图数据,进行地理信息分析和处理。 使用黄河九地图数据Shp 可以进行各种有关黄河流域的地理信息分析和处理。例如,分析黄河流域内不同地区的地形、气候、土壤、地质等因素对河流水质、水量、水文周期以及水文气候等方面的影响。此外,还可以计算不同区域内的水资源量, 分析地区可能面临的水资源风险, 同时可以对水资源进行合理规划与管理。 总之,黄河九地图数据Shp为黄河流域日常采集和分析水文资料提供了有力支持, 同时也可以支持城市建设和工程规划等方面的分析。因此,黄河九地图数据Shp在黄河流域及周边地区的管理和规划等领域中具有广泛的应用前景。 ### 回答2: 黄河九地图数据shp是一种包含有关中国黄河流域九个份地理信息的数字地图文件。这些份包括:青海、甘肃、宁夏、内蒙古、陕西、山西、河南、山东和河北。该地图文件将这些份的边界和地理要素转换为几何图形,并将其存储在shp文件中,可实现对其进行空间分析和地图作。 这个地图数据shp文件实用性很高,可以广泛应用于黄河流域相关的地理信息领域。例如,在自然资源管理方面,可以使用它来进行土地资源、水资源、气候资源和矿产资源的分析,以便有效管理和利用这些资源。而在交通运输、城市规划和环境保护方面,该文件可以用于路线规划、区域规划和环境监测,提高规划决策的准确性和精度。 此外,该地图数据shp文件可以应用在教育和科研领域,如地理信息系统、地理编码和遥感分析等方面,为学术研究和教学活动提供相关的数据支持和参。总之,黄河九地图数据shp文件是非常有价值的地理信息资源,对于研究和应用黄河流域相关的地理信息问题非常有帮助。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值