Codeforces Round #349 (Div. 1) C E (计数. SAM+线段树)

6 篇文章 0 订阅
5 篇文章 0 订阅

 C Codeword


题意:有多个询问,问长度为n包含当前串s为子序列的字符串有多少种


ans = ΣC(i-1, len-1) * 25^(i-len) * 26 ^ (n-i)

离线随便搞搞


#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <bitset>
#include <queue>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;

#define LL long long
#define ULL unsigned long long
#define eps 1e-9
#define N (200000 + 10)
#define M (400000 + 10)
#define pii pair<int,int>
#define MP make_pair
#define inf 0x3f3f3f3f
#define lson ll, md, ls
#define rson md + 1, rr, rs
#define mod 1000000007


int f[N], nf[N];
int p1[N], p2[N];
int qpow(int x, int y) {
	int ret = 1;
	while(y) {
		if(y & 1) ret = (LL) ret * x % mod;
		x = (LL)x * x % mod;
		y >>= 1;
	}
	return ret;
}

void init() {
	f[0] = 1, nf[0] = 1;
	for(int i = 1; i < N; ++i)
		f[i] = (LL) f[i-1] * i % mod;
	nf[N-1] = qpow(f[N-1], mod-2);
	for(int i = N-2; i > 0; --i) 
		nf[i] = (LL) nf[i+1] * (i+1) % mod;

	p1[0] = p2[0] = 1;
	for(int i = 1; i < N; ++i)
		p1[i] = (LL) p1[i-1] * 25 %mod,
		p2[i] = (LL) p2[i-1] * 26 % mod;

}

int C(int n, int m) {
	if(n < m) return 0;
	return (LL) f[n] * nf[n-m] % mod * nf[m]% mod;
}
int len;
int get(int i) {
	return (LL) C(i-1, len-1) * p1[i-len] % mod;
}
char s[N];
struct node {
	int op, val, l, id;
	int ret;
	bool operator < (const node &ot) const{
		return l < ot.l || (l == ot.l && val < ot.val);
	}
}a[N];

int last;
LL ans;

void cal(int &n) {
	ans = (26 * ans % mod + (LL)get(n+1) % mod) % mod;
	++n;
}
bool cmp(node a, node b) {
	return a.id < b.id;
}

void RE() {
	vector<int> vt;
	vt[0] = -1;
}

int main() {
	//freopen("in.txt", "r", stdin);
	int m;
	init();
	scanf("%d", &m);
	scanf("%s", s);
	len = strlen(s);
	for(int i = 0; i < m; ++i) {
		scanf("%d", &a[i].op);
		a[i].id = i;
		if(a[i].op == 1) {
			scanf("%s", s);
			len = strlen(s);
		}
		else {
			scanf("%d", &a[i].val);
		}
		a[i].l = len;
	}
	sort(a, a + m);
	ans = 0; 
	for(int i = 0; i < m; ++i) {
		if(a[i].op == 0) continue;
		if(i == 0 || a[i].l != a[i-1].l) ans = 0, last = 0, len = a[i].l;
		
		while(last < a[i].val) cal(last);
		a[i].ret = ans;
	}
	sort(a, a + m, cmp);
	for(int i = 0; i < m; ++i)
		if(a[i].op == 2) printf("%d\n", a[i].ret);
}


Forensic Examination

题意:给一个大串s和m个小串,每次询问大串的子串s[sl,sr]在[l,r]之间这些小串中出现次数最多的是哪个小串


对所有小串做一次sam,对s的每个后缀先记录对应节点及往前能匹配多长,然后倍增找到s[sl,sr]对应的结点u。

考虑计算s在t中出现了多少次只需要在s所在结点的parent tree子树中计算包含了几个t的前缀

于是需要在u的子树中统计包含了多少个每个小串的前缀,每个结点建一颗线段树保存,由于sam每个结点最多包含一个小串的一个前缀,可以从parent tree叶子节点往上,先把当前结点表示的前缀加入,然后向上做线段树合并。

对于询问就在线段树中查询l到r最大值即可。


#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <bitset>
#include <queue>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;

#define LL long long
#define ULL unsigned long long
#define eps 1e-9
#define N (2000000 + 10)
#define M (8000000 + 10)
#define pii pair<int,int>
#define MP make_pair
#define inf 0x3f3f3f3f
#define lson ll, md, ls
#define rson md + 1, rr, rs
#define mod 1000000007

int n,m;
struct Query{
	int l, r, id;
	Query() {}
	Query(int l, int r, int id) :l(l), r(r), id(id) {}
};

struct state {
	int val, id;
	state *son[27], *par[30];
	vector<Query> Que;
	int rt;
}que[N], *root, *last;
int tot;

void extend(int w, int id) {
	state *p = last;
	state *np = &que[tot++];
	np->val = p->val + 1;
	while(p && p->son[w] == 0) {
		p->son[w] = np, p = p->par[0];
	}
	if(p == 0) {
		np->par[0] = root;
	}
	else {
		state *q = p->son[w];
		if(q->val == p->val + 1) 
			np->par[0] = q;
		else {
			state *nq = &que[tot++];
			memcpy(nq->son, q->son, sizeof q->son);
			nq->par[0] = q->par[0];
			nq->val = p->val + 1;
			np->par[0] = q->par[0] = nq;
			while(p && p->son[w] == q) {
				p->son[w] = nq, p = p->par[0];
			}
		}
	}
	np->id = id;
	last = np;
}
char s[N], t[N];
state * match[N]; int mlen[N];
pii ans[N];
int rk[N], cnt[N];


void par_init() {
	for(int j = 1; j <= 20; ++j) 
		for(int i = 0; i < tot; ++i)
			if(que[i].par[j-1])
				que[i].par[j] = que[i].par[j-1]->par[j-1];
}

void s_init() {
	state *cur = root;
	int tl = 0;
	for(int i = 0; i < n; ++i) {
		int c = s[i]-'a'+1;
		while(cur && cur->son[c] == NULL) {
			cur = cur->par[0];
			if(cur)
				tl = cur->val;
			else tl = 0;
		}
		if(cur && cur->son[c]) {
			
			mlen[i] = ++tl;
			cur = cur->son[c];
			match[i] = cur;
		}
		else match[i] = NULL, cur = root;
	}
}

state * find(state *st, int len) {
	state *cur = st;
	for(int i = 20; i >= 0; --i) {
		if(cur->par[i] && cur->par[i]->val >= len) 
			cur = cur->par[i];
	}
	return cur;
}
int tcnt;
int mx[M], ls[M], rs[M], who[N];

void up(int i) {
	if(mx[ls[i]] > mx[rs[i]]) {
		mx[i] = mx[ls[i]];
		who[i] = who[ls[i]];
	}
	else if(mx[ls[i]] == mx[rs[i]]) {
		mx[i] = mx[ls[i]];
		who[i] = min(who[ls[i]], who[rs[i]]);
	}
	else {
		mx[i] = mx[rs[i]];
		who[i] = who[rs[i]];
	}

}

int update(int u, int l, int r, int id) {
	if(u == 0) u = ++tcnt;
	if(l == r) {
		mx[u] += 1;
		who[u] = id;
		return u;
	}
	int mid = l + r >> 1;
	if(id <= mid) ls[u] = update(ls[u], l, mid, id);
	else rs[u] = update(rs[u], mid+1, r, id);
	up(u);
	return u;
}

pii query(int u, int l, int r, int ll, int rr) {
	if(u == 0) return MP(ll, 0);
	if(l == ll && r == rr) {
		return MP(who[u], mx[u]);
	}
	int md = l+r >> 1;
	pii ret;
	if(rr <= md) ret = query(ls[u], l, md, ll, rr);
	else if(ll > md) ret = query(rs[u], md+1,r, ll, rr);
	else {
		pii ts = query(ls[u], l, md, ll, md);
		pii rt = query(rs[u], md+1, r, md+1, rr);
		if(ts.second >= rt.second) ret = ts;
		else ret = rt;
	}
	return ret;
}

int merge(int u, int v, int l, int r) {
	if(u == 0 || v == 0) return u ^ v;
	if(l == r) {
		mx[u] += mx[v];
		who[u] = l;
		return u;
	}
	int md = l + r >> 1;
	ls[u] = merge(ls[u], ls[v], l, md);
	rs[u] = merge(rs[u], rs[v], md+1, r);
	up(u);
	return u;
}

state * check(int l, int r) {
	state *cur = root;
	for(int i = l; i <= r; ++i)
		cur = cur->son[s[i]-'a'+1];
	return cur;
}

int main() {
	//freopen("in.txt", "r", stdin);
	scanf("%s", s);
	n = strlen(s);
	scanf("%d", &m);
	root = last = &que[tot++];
	for(int i = 1; i <= m; ++i)
	{
		scanf("%s", t);
		int len = strlen(t);
		for(int j = 0; j < len; ++j)
			extend(t[j]-'a'+1, i);
		extend(0, 0);
	}

	par_init();
	s_init();
	int q;
	scanf("%d", &q);

	for(int i = 1; i <= q; ++i) {
		int l, r, sl,sr;
		scanf("%d%d%d%d", &l, &r, &sl, &sr);
		--sl, --sr;
		state *cur = NULL;
		if(mlen[sr] >= sr-sl+1) cur = find(match[sr], sr-sl+1);
		
		if(cur == NULL) ans[i] = MP(l, 0);
		else cur->Que.push_back(Query(l, r, i));
	}

	//topo
	for(int i = 0; i < tot; ++i) ++cnt[que[i].val];
	for(int i = 1; i <= tot; ++i) cnt[i] += cnt[i-1];
	for(int i = 0; i < tot; ++i) rk[--cnt[que[i].val]] = i;

	//cal
	for(int u = tot-1; u > 0; -- u) {
		int i = rk[u];
		if(que[i].id != 0) 
			que[i].rt = update(que[i].rt, 1, m, que[i].id);
		for(int j = 0; j < que[i].Que.size(); ++j) {
			Query tmp = que[i].Que[j];
			ans[tmp.id] = query(que[i].rt, 1, m, tmp.l, tmp.r);
		}
		if(que[i].par[0]) 
			que[i].par[0]->rt = merge(que[i].par[0]->rt, que[i].rt, 1, m);
	}

	for(int i = 1; i <= q; ++i)
		printf("%d %d\n", ans[i].first, ans[i].second);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值