牛客 - Elo mountains(AC自动机+可持久化数组优化)

题目链接:点击查看

题目分析:初始时给出一棵以点 0 0 0 为根节点的字典树,设 a r r i arr_i arri 为从根节点出发到达点 i i i 的字符串,需要回答对于每个 i ∈ [ 1 , n ] i\in[1,n] i[1,n] 时的 ∑ k = 1 n f ( a r r i , a r r k ) \sum_{k=1}^{n}f(arr_i,arr_k) k=1nf(arri,arrk),其中 f ( s , t ) f(s,t) f(s,t) 代表的是字符串 s s s 在字符串 t t t 中出现的次数

题目分析:
先附上官方题解:
牛客 - Elo mountains(AC自动机+可持久化数组优化)_第1张图片
本题难点及解决方法:

  1. 字符集过大(可持久化数组优化getfail)
  2. 如何统计答案:一个串在另一个串中出现,只可能是两种情况:
    1. 作为前缀:直接统计字典树中某个节点的子树大小即可
    2. 作为中间的位置:所有能够沿着 f a i l fail fail 边跳到当前节点的点的总和,建出 f a i l fail fail 树然后 d p dp dp 一下即可

需要注意的一个小坑点就是,因为字典树上的字符串总长最大可能是 O ( n 2 ) O(n^2) O(n2) 级别的,所以答案可能会爆 i n t int int,记得开 l o n g   l o n g long\ long long long

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

代码:

// Problem: Elo mountains
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/17148/G
// Memory Limit: 1048576 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
     
	T f=1;x=0;
	char ch=getchar();
	while(0==isdigit(ch)){
     if(ch=='-')f=-1;ch=getchar();}
	while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	x*=f;
}
template<typename T>
inline void write(T x)
{
     
	if(x<0){
     x=~(x-1);putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e5+100;
const int M=1e5;
vector<pair<int,int>>edge[N];//原树
vector<int>node[N];//fail树
unordered_map<int,int>trans[N];//原树的字典树
struct Node {
     
	int l,r,val;
}tree[N*20];//可持久化数组
int trie[N],fail[N],tot;
LL dp[N],sz[N];
int newnode() {
     
	tot++;
	tree[tot].l=tree[tot].r=tree[tot].val=0;
	return tot;
}
void add(int &k,int pos,int val,int l,int r) {
     
	int nk=newnode();
	tree[nk]=tree[k];
	k=nk;
	if(l==r) {
     
		tree[k].val+=val;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) {
     
		add(tree[k].l,pos,val,l,mid);
	} else {
     
		add(tree[k].r,pos,val,mid+1,r);
	}
}
int ask(int k,int pos,int l,int r) {
     
	if(l==r) {
     
		return tree[k].val;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) {
     
		return ask(tree[k].l,pos,l,mid);
	} else {
     
		return ask(tree[k].r,pos,mid+1,r);
	}
}
void getfail() {
     
	queue<int>q;
	for(auto it:trans[0]) {
     
		int u=0,v=it.second,to=it.first;
		add(trie[u],to,v,1,M);
		fail[v]=u;
		q.push(v);
	}
	while(q.size()) {
     
		int u=q.front();
		q.pop();
		trie[u]=trie[fail[u]];
		for(auto it:trans[u]) {
     
			int v=it.second,to=it.first;
			int delta=v-ask(trie[u],to,1,M);
			add(trie[u],to,delta,1,M);
			fail[v]=ask(trie[fail[u]],to,1,M);
			q.push(v);
		}
	}
}
void buildfail(int n) {
     
	for(int i=1;i<=n;i++) {
     
		dp[fail[i]]+=sz[i];
		node[fail[i]].push_back(i);
	}
}
void dfs1(int u,int fa) {
     
	sz[u]=1;
	for(auto it:edge[u]) {
     
		int v=it.first,to=it.second;
		if(v==fa) {
     
			continue;
		}
		trans[u][to]=v;
		dfs1(v,u);
		sz[u]+=sz[v];
	}
}
void dfs2(int u) {
     
	for(auto v:node[u]) {
     
		dfs2(v);
		dp[u]+=dp[v];
	}
}
void init() {
     
	tot=-1;
	newnode();
}
int main()
{
     
#ifndef ONLINE_JUDGE
//	freopen("data.in.txt","r",stdin);
//	freopen("data.out.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	init();
	int n;
	read(n);
	for(int i=1;i<=n;i++) {
     
		int u,v,w;
		read(u),read(v),read(w);
		edge[u].push_back({
     v,w});
		edge[v].push_back({
     u,w});
	}
	dfs1(0,-1);//建字典树
	getfail();//得到fail边
	buildfail(n);//建fail树
	dfs2(0);//fail树上dp
	for(int i=1;i<=n;i++) {
     
		printf("%lld\n",dp[i]+sz[i]);
	}
	return 0;
}

你可能感兴趣的:(字符串处理,主席树)