您的位置:首页 > 其它

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

2016-05-14 20:23 169 查看
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);
}


E 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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: