【省选模拟】20/02/07 (JSOI2015)

在这里插入图片描述

  • 思路挺好的,但是证明可以被打表踩爆
    每个元素是独立的,如果我们算出一种元素的方案数 f ( k ) f(k) 那么最终答案就会是 f ( k ) n f(k)^n
    这一步转换比较巧妙,把集合分为每种元素分别处理
    考虑递推 f ( k ) f(k) ,每次加入最下面一排,那么最下面一排肯定是前缀若干个 1 或是没有 1,枚举个数,这些 1 上面肯定全部都是 1,左边是剩余的三角形,那么
    f ( k ) = 1 + i = 1 k f ( k i ) = 2 k f(k)=1+\sum_{i=1}^kf(k-i)=2^k
    a n s = 2 n k ans=2^{nk}

在这里插入图片描述

  • 如果没有 L L 的限制那么最大最小取到两个端点,可以用分数规划解决
    如果有 L L 的限制那么我们会贪心取长度为 L L 的区间
    复杂度 O ( n l o g A ) O(nlogA)

在这里插入图片描述
在这里插入图片描述

  • 考场上做着做着就把前缀做成了字符串匹配,所以我来说一下如果是字符串匹配可以怎么做
    首先一个在线的做法是我们树剖,然后用线段树维护每个区间的 A C AC 自动机
    这么做的空间是 S l o g ( n ) 26 \sum |S|log(n)*26 从空间开始就凉掉了,时间 S l o g ( n ) 2 \sum |S|log(n)^2 ,十分不优秀
    于是我就考虑不对树上的串建自动机,而是对询问串建 A C AC 自动机,把树上的串拿过来匹配
    于是问题就变成求 f a i l fail 树的子树中有没有出现某几种颜色的点,有没有出现某种颜色可以用树剖拆成 l o g ( n ) log(n) 个有没有出现 [ L , R ] [L,R] 的颜色
    一开始想的是 d f s dfs 序转换然后用个数颜色的套路,然后发现颜色本身还有个区间的限制,于是树套树?
    后来发现用线段树维护颜色集合然后合并上去就可以做了
    这么做的复杂度是 O ( Q l o g ( n ) 2 ) O(Qlog(n)^2)
  • 然后在我写完过后发现求的是一个前缀有没有出现
    于是对询问建 t r i e trie ,离线,动态插入一条到根的链的所有字符串
    u , v u,v 的答案减去 2 l c a 2*lca 的答案,复杂度 O ( Q l o g ( n ) + S ) O(Qlog(n)+\sum|S|)

AC自动机 + 线段树合并的代码(不知道对不对)

#include<bits/stdc++.h>
#define cs const
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;
}
cs int N = 1e5 + 50;
int n, q, fi[N], nxt[N<<1], to[N<<1], tot;
string w[N<<1];
void add(int x, int y, string S){ 
	nxt[++tot] = fi[x], fi[x] = tot;
	to[tot] = y; w[tot] = S;
}
string S[N]; int ans[N]; 

int sz[N],dep[N],fa[N],son[N],in[N],top[N],sgn;
void pre_dfs(int u, int f){
	sz[u] = 1; 
	for(int i = fi[u]; i; i = nxt[i]){
		int t = to[i]; if(t == f) continue;
		dep[t]=dep[u]+1; fa[t]=u; S[t]=w[i]; pre_dfs(t,u);
		sz[u]+=sz[t]; if(sz[t]>sz[son[u]]) son[u]=t;
	}
}
void dfs(int u, int tp){
	top[u] = tp; in[u]=++sgn;
	if(son[u]) dfs(son[u],tp);
	for(int i = fi[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa[u] || t == son[u]) continue; dfs(t,t);
	}
}

#define pb push_back
struct query{ 
	int l, r, c; 
	query(int _l=0, int _r=0, int _c=0){ l = _l; r = _r; c = _c; }
}; 

namespace SGT{
	int nd, ls[N], rs[N], sz[N];
	#define mid ((l+r)>>1)
	void pushup(int x){ sz[x] = sz[ls[x]] + sz[rs[x]]; }
	void ins(int &x, int l, int r, int p){
		if(!x) x = ++nd; if(l == r){ sz[x] = 1; return; }
		if(p<=mid) ins(ls[x],l,mid,p);
		else ins(rs[x],mid+1,r,p); pushup(x);
	}
	void merge(int &x, int y, int l, int r){
		if(!x || !y){ x = x|y; return; }
		if(l == r){ sz[x] = 1; return; }
		merge(ls[x],ls[y],l,mid);
		merge(rs[x],rs[y],mid+1,r); 
		pushup(x);
	}
	int query(int x, int l, int r, int L, int R){
		if(!x) return 0; if(L<=l && r<=R) return sz[x]; int ans = 0;
		if(L<=mid) ans+=query(ls[x],l,mid,L,R);
		if(R>mid) ans+=query(rs[x],mid+1,r,L,R); return ans;
	}
}

namespace AC{
	cs int N = ::N * 10;
	int ch[N][26], fail[N], nd; vector<query> qry[N];
	int ins(char *S){
		int len = strlen(S), now = 0;
		for(int i = 0; i < len; i++){
			int c = S[i]-'a'; if(!ch[now][c]) ch[now][c] = ++nd;
			now = ch[now][c];
		} return now;
	}
	vector<int> G[N];
	void build(){
		queue<int> q; 
		for(int i = 0; i < 26; i++) if(ch[0][i]) q.push(ch[0][i]);
		while(!q.empty()){
			int x = q.front(); q.pop();
			for(int i = 0; i < 26; i++){
				if(!ch[x][i]) ch[x][i] = ch[fail[x]][i];
				else fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
			}
		} for(int i = 1; i <= nd; i++) G[fail[i]].pb(i), cout << fail[i] << endl;
	}
	vector<int> col[N];
	void putcol(string S, int c){
		int len = S.length(), now = 0;
		for(int i = 0; i < len; i++){
			now = ch[now][S[i]-'a']; 
			col[now].pb(c);
		}
	}
	int rt[N];
	void work(int u){
		for(int e = 0; e < G[u].size(); e++){
			int v = G[u][e]; work(v);
			SGT::merge(rt[u],rt[v],1,n);
		}
		for(int i = 0; i < col[u].size(); i++) 
			SGT::ins(rt[u],1,n,col[u][i]);
		for(int i = 0; i < qry[u].size(); i++){
			query now = qry[u][i];
			ans[now.c] += SGT::query(rt[u],1,n,now.l,now.r);
		}
	}
};

void pushqry(int c, int ps, int u, int v){
	while(top[u] ^ top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		AC::qry[ps].pb(query(in[top[u]],in[u],c));
		u = fa[top[u]];
	}
	if(in[u] > in[v]) swap(u,v); 
	if(in[u] < in[v]) AC::qry[ps].pb(query(in[u]+1,in[v],c));
}

int main(){
	n = read(); char s[10]; 
	for(int i = 1, x, y; i < n; i++){
		x = read(), y = read(); scanf("%s",s);
		string S = s; add(x,y,S); add(y,x,S);
	} pre_dfs(1,0); dfs(1,1); 
	q = read();
	for(int i = 1; i <= q; i++){
		int u = read(), v = read(); scanf("%s",s); 
		int nx = AC::ins(s);
		pushqry(i, nx, u, v);
	} 
	AC::build();
	for(int i = 2; i <= n; i++) AC::putcol(S[i],in[i]);
	AC::work(0);
	for(int i = 1; i <= q; i++) cout << ans[i] << '\n';
	return 0;
}
#include<bits/stdc++.h>
#define pb push_back
#define cs const
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;
}
cs int N = 1e5 + 50;
int n, q, fi[N], nxt[N<<1], to[N<<1], tot;
string w[N<<1];
void add(int x, int y, string S){ 
	nxt[++tot] = fi[x], fi[x] = tot;
	to[tot] = y; w[tot] = S;
}
int ans[N]; 
struct query{ 
	int u, op, c; 
	query(int _u=0, int _op=0, int _c=0){ u = _u; op = _op; c = _c; }
}; vector<query> qry[N];
int sz[N],dep[N],son[N],fa[N],top[N];
void pre_dfs(int u, int f){
	sz[u] = 1; 
	for(int i = fi[u]; i; i = nxt[i]){
		int t = to[i]; if(t == f) continue;
		dep[t]=dep[u]+1; fa[t]=u; pre_dfs(t,u);
		sz[u]+=sz[t]; if(sz[t]>sz[son[u]]) son[u]=t;
	} 
}
void dfs(int u, int tp){
	top[u] = tp; if(son[u]) dfs(son[u],tp);
	for(int i = fi[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa[u] || t == son[u]) continue; dfs(t,t);
	}
}
int lca(int x, int y){
	while(top[x] ^ top[y]){
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		x = fa[top[x]];
	} return dep[x] < dep[y] ? x : y;
}
namespace AC{
	cs int N = ::N * 10;
	int ch[N][26], ct[N], nd;
	int ins(char *S){
		int len = strlen(S), now = 0;
		for(int i = 0; i < len; i++){
			int c = S[i]-'a'; if(!ch[now][c]) ch[now][c] = ++nd;
			now = ch[now][c];
		} return now;
	}
	void putcol(string S, int v){
		int len = S.length(), now = 0;
		for(int i = 0; i < len; i++){
			now = ch[now][S[i]-'a']; if(!now) return;
			ct[now] += v;  
		} 
	}
}
void work(int u, int fa){
	for(int i = 0; i < qry[u].size(); i++){
		query now = qry[u][i]; 
		ans[now.c] += now.op * AC::ct[now.u];
	}
	for(int i = fi[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		AC::putcol(w[i], 1);
		work(t, u);
		AC::putcol(w[i], -1); 
	}
}
int main(){
	freopen("strings.in","r",stdin);
	freopen("strings.out","w",stdout);
	n = read(); char s[10]; 
	for(int i = 1, x, y; i < n; i++){
		x = read(), y = read(); scanf("%s",s);
		string S = s; add(x,y,S); add(y,x,S);
	} pre_dfs(1, 0); dfs(1, 1);
	q = read();
	for(int i = 1; i <= q; i++){
		int u = read(), v = read(); scanf("%s",s); 
		int nx = AC::ins(s);
		qry[u].pb(query(nx, 1, i));
		qry[v].pb(query(nx, 1, i));
		qry[lca(u, v)].pb(query(nx, -2, i));
	} work(1, 0);
	for(int i = 1; i <= q; i++) cout << ans[i] << '\n';
	return 0;
}
发布了634 篇原创文章 · 获赞 98 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/104209150