JZOJ 5167 【NOIP2017模拟6.26】下蛋爷

来源:互联网 发布:windows图形编程原理 编辑:程序博客网 时间:2024/04/28 08:04

题目大意:

有n个单词,有一个长度为m的文章,现在你先需要知道每个单词在文章内出现的次数。
现在一共进行k轮,每一轮有p的概率保留出现次数最小的那些单词(不保留就是把那些单词删掉)。
求n轮以后,每个单词保留下来的概率.

1<=n<=200,1<=单词长度<=20,1<=m<=10^6

题解:

分两个部分。
第一部分就直接上ac自动机,可以打up来优化。
ac自动机up优化:
up[x]表示x和x的fail链上的第一个是单词结尾的点。
这样查询的时候就会很快(贴瓷砖也有用的上)。
注意单词可能会有重复,单词结尾需要用前向星维护。

第二部分就是概率dp.
设f[i][j]表示第i轮,出现次数第j大的单词们活下来的概率(出现次数相同的算同一个)
f[i][j] = f[i - 1][j - 1] +f[i - 1][j] * p

Code:

#include<cstdio>#include<string>#include<cstring>#include<algorithm>#define fo(i, x, y) for(int i = x; i <= y; i ++)using namespace std;const int Maxn = 205, Maxm = 1000005;double p;char a[Maxn][25], s[Maxm];int n, ls, cc, ans[205], b[205], d[10000], next[10000][27], fail[10000], up[10000], tt;int final[10000], tot;struct edge {    int to, next;}e[205];double f[1005][205], sum[1005];void link(int x, int y) {    e[++ tot].next = final[x], e[tot].to = y, final[x] = tot;   }void insert(int p, char *a) {    int len = strlen(a + 1), k = 1;    fo(i, 1, len) {        if(!next[k][a[i] - 'a']) next[k][a[i] - 'a'] = ++ tt;        k = next[k][a[i] - 'a'];    }    link(k, p);}void make_ac() {    int l = 0, r = 0; d[++ r] = 1;    while(l < r) {        l ++; int x = d[l];        fo(i, 0, 25) {            int y = next[x][i];            if(!y) continue;            int j = fail[x];            while(j && !next[j][i]) j = fail[j];            if(next[j][i]) j = next[j][i];            fail[y] = j; if(!fail[y]) fail[y] = 1;            up[y] = y; if(!final[y] && y != 1) up[y] = up[fail[y]];            d[++ r] = y;        }    }}void Init() {    tt = 1;    scanf("%d", &n);    fo(i, 1, n) {        scanf("%s", a[i] + 1);        insert(i, a[i]);    }    make_ac();    scanf("%s", s + 1);    ls = strlen(s + 1);}void Work() {    int j = 1;    fo(i, 1, ls) {        while(j != 1 && !next[j][s[i] - 'a']) j = fail[j];        if(next[j][s[i] - 'a']) j = next[j][s[i] - 'a'];        int z = up[j];        while(z) {            for(int k = final[up[z]]; k; k = e[k].next)                ans[e[k].to] ++;            z = up[fail[z]];        }    }}bool rank_b(int x, int y) {    return ans[x] < ans[y]; }void End() {    scanf("%lf", &p); scanf("%d", &cc);    fo(i, 1, n) b[i] = i;    sort(b + 1, b + n + 1, rank_b);    fo(j, 2, n) f[1][j] = 1;    f[1][1] = p;    fo(i, 2, cc) {        int k = 0;        fo(j, 1, n) {            if(j == 1 || ans[b[j]] != ans[b[j - 1]]) {                k ++;                f[i][k] = f[i - 1][k - 1] + (f[i - 1][k] - f[i - 1][k - 1]) * p;            }        }    }    int k = 0;    fo(i, 1, n) {        if(i != 1 && ans[b[i]] == ans[b[i - 1]])            sum[b[i]] = sum[b[i - 1]]; else k ++, sum[b[i]] = f[cc][k];    }    fo(i, 1, n) printf("%.3lf ", sum[i]);}int main() {    Init();    Work();    End();}
阅读全文
0 0