「雅礼集训 2017 Day7」事情的相似度(后缀自动机+LCT+树状数组)

6 篇文章 0 订阅
6 篇文章 1 订阅

description

点击查看题目内容

solution

Step1
无脑建 S A M SAM SAM
两个前缀的最长公共后缀就是 p a r e n t − t r e e parent-tree parenttree上两点的 l c a lca lca,定义知显然


Step2
离线询问,按右端点从小到大排序


Step3
每加入一个字母,就将 t a ta ta p a r e n t − t r e e parent-tree parenttree上到根节点的路径打上 t a ta ta的标记
如果遇到以前打的标记,则说明该节点即为旧标记与新标记的 l c a lca lca
贪心把标记覆盖为较大的值


Step4
树状数组统计
下标为左端点,每次查询下标大于等于该询问左端点的最大深度
向根跑的过程中每一次遇到旧标记,就在树状数组上更新答案,并给该节点打上新标记
但树状数组是用来计算前缀和的,所以有一个 n − x + 1 n-x+1 nx+1的小转化


Step5
往根节点跑的过程即是 L C T LCT LCT a c c e s s access access操作


code

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 200005
struct node {
	int f, val, tag;
	int son[2];
}tree[maxn];
struct SAM {
	int fa, len;
	int son[2];
}t[maxn];
vector < pair < int, int > > G[maxn];
int last = 1, cnt = 1, n, m;
char s[maxn];
int ans[maxn], maxx[maxn], num[maxn], st[maxn];

int lowbit( int x ) {
	return x & ( -x );
}

void modify( int x, int val ) {
	for( int i = n - x + 1;i <= n;i += lowbit( i ) )
		maxx[i] = max( maxx[i], val );
}

int query( int x ) {
	int ans = 0;
	for( int i = n - x + 1;i;i -= lowbit( i ) )
		ans = max( ans, maxx[i] );
	return ans;
}

void insert( int id, int x ) {
	int pre = last, now = last = ++ cnt;
	num[id] = cnt;
	t[now].len = t[pre].len + 1;
	while( pre && ! t[pre].son[x] ) t[pre].son[x] = now, pre = t[pre].fa;
	if( ! pre ) t[now].fa = 1;
	else {
		int u = t[pre].son[x];
		if( t[pre].len + 1 == t[u].len ) t[now].fa = u;
		else {
			int v = ++ cnt;
			t[v] = t[u];
			t[v].len = t[pre].len + 1;
			t[u].fa = t[now].fa = v;
			while( pre && t[pre].son[x] == u ) t[pre].son[x] = v, pre = t[pre].fa;
		}
	}
}

bool isroot( int x ) {
	return tree[tree[x].f].son[0] == x || tree[tree[x].f].son[1] == x;
}

void rotate( int x ) {
	int fa = tree[x].f, Gfa = tree[fa].f;
	int k = tree[fa].son[1] == x;
	if( isroot( fa ) )
		tree[Gfa].son[tree[Gfa].son[1] == fa] = x;
	tree[x].f = Gfa;
	if( tree[x].son[k ^ 1] )
		tree[tree[x].son[k ^ 1]].f = fa;
	tree[fa].son[k] = tree[x].son[k ^ 1];
	tree[x].son[k ^ 1] = fa;
	tree[fa].f = x;
}

void change( int x, int val ) {
	tree[x].val = tree[x].tag = val;
}

void pushdown( int x ) {
	if( ! tree[x].tag ) return;
	if( tree[x].son[0] ) change( tree[x].son[0], tree[x].tag );
	if( tree[x].son[1] ) change( tree[x].son[1], tree[x].tag );
	tree[x].tag = 0;
}

void splay( int x ) {
	int Top = 0, y = x;
	st[++ Top] = y;
	while( isroot( y ) ) st[++ Top] = y = tree[y].f;
	while( Top ) pushdown( st[Top --] );
	while( isroot( x ) ) {
		int fa = tree[x].f, Gfa = tree[fa].f;
		if( isroot( fa ) )
			( ( tree[Gfa].son[0] == fa ) ^ ( tree[fa].son[0] == x ) ) ? rotate( x ) : rotate( fa );
		rotate( x );
	}
}

void access( int x, int val ) {
	int son;
	for( son = 0;x;son = x, x = tree[x].f ) {
		splay( x );
		modify( tree[x].val, t[x].len );
		tree[x].son[1] = son;
	}
	tree[son].tag = tree[son].val = val;
}

int main() {
	scanf( "%d %d %s", &n, &m, s + 1 );
	for( int i = 1;i <= n;i ++ ) //树状数组下标必须从1开始 
		insert( i, s[i] - '0' );
	for( int i = 1, l, r;i <= m;i ++ ) {
		scanf( "%d %d", &l, &r );
		G[r].push_back( make_pair( l, i ) );
	}
	for( int i = 1;i <= cnt;i ++ ) tree[i].f = t[i].fa;
	for( int i = 1;i <= n;i ++ ) {
		access( num[i], i );
		for( int j = 0;j < G[i].size();j ++ )
			ans[G[i][j].second] = query( G[i][j].first );
	}
	for( int i = 1;i <= m;i ++ )
		printf( "%d\n", ans[i] );
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值