(fzu)Problem I Magic(模拟+后缀匹配)

来源:互联网 发布:森系服装品牌 知乎 编辑:程序博客网 时间:2024/06/04 18:55

Problem Description
Kim is a magician, he can use n kinds of magic, number from 1 to n. We use string Si to describe magic i. Magic Si will make Wi points of damage. Note that Wi may change over time.
Kim obey the following rules to use magic:
Each turn, he picks out one magic, suppose that is magic Sk, then Kim will use all the magic i satisfying the following condition:
1. Wi<=Wk
2. Sk is a suffix of Si.
Now Kim wondering how many magic will he use each turn.

Note that all the strings are considered as a suffix of itself.
Input
First line the number of test case T. (T<=6)
For each case, first line an integer n (1<=n<=1000) stand for the number of magic.
Next n lines, each line a string Si (Length of Si<=1000) and an integer Wi (1<=Wi<=1000), stand for magic i and it’s damage Wi.
Next line an integer Q (1<=Q<=80000), stand for there are Q operations. There are two kinds of operation.
“1 x y” means Wx is changed to y.
“2 x” means Kim has picked out magic x, and you should tell him how many magic he will use in this turn.
Note that different Si can be the same.
Output
For each query, output the answer.
Sample Input
1
5
abracadabra 2
adbra 1
bra 3
abr 3
br 2
5
2 3
2 5
1 2 5
2 3
2 2
Sample Output
3
1
2
1

分析:模拟题
这道题关键在于匹配字符串的后缀,所以可能一不小心时间超限,字符串匹配的算法,匹配后缀,猜想后缀数组可以吧,只是这个比较高级,在挑战程序。。。。中的最后才讲,目前不会。。。
这个题我写得是超限了,问了下AC的大佬,他是模拟卡过去了,看了下好像是我的循环查找的地方有一点没处理好,没记录长度,每次调用strlen()函数,所以会时间超限。。改了一下,AC了(1937ms) 刚好卡过。。
用树做很快,从学长的题解报告知道,可以用字典树。 先给自己普及下做法。。 过段时间再写。。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=1005;int n,q;struct Node{    char a[N];    int cnt;///题中的Wi    int len;///字符串a的长度} s[N];int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        for(int i=1; i<=n; i++)        {            scanf("%s%d",s[i].a,&s[i].cnt);            s[i].len=strlen(s[i].a);        }        scanf("%d",&q);        int c,x,y;        for(int i=0; i<q; i++)        {            scanf("%d",&c);            if(c==1)            {                scanf("%d%d",&x,&y);                s[x].cnt=y;               // printf("%d\n",s[x].cnt);            }            else            {                scanf("%d",&x);                //printf("%d\n",s[x].cnt);                int num=1;                for(int i=1; i<=n; i++)                {                    if(i==x) continue;                    int flag;                    if(s[i].cnt<=s[x].cnt&&s[i].len>=s[x].len)                    {                        flag=1;                        for(int j=s[i].len-1,k=s[x].len-1; k>=0; j--,k--)                        {                            //printf("%c %c\n",s[i].a[j],s[x].a[k]);                            if(s[i].a[j]!=s[x].a[k])                            {                                flag=0;                                break;                            }                        }                        if(flag) num++;                    }                }                printf("%d\n",num);            }        }    }    return 0;}

思路:把字符串反转就转化为前缀的问题,这样就可以用字典树来处理是否为前缀问题
然后再用树状数组来维护数量
具体做法是把字符串按长度从小到大插入字典树
对于第i次插入, 若某个结点是结尾结点,则将该点的树状数组更新
因为以该点结尾的字符串是当前插入字符串的前缀
查询操作就是查询第k个字符串结尾的点的树状数组1到w[k]的和
修改操作同样和插入操作类似,把路径上结尾结点的所有树状数组更新
由于只有n个字符串,开n颗树状数组就够了
查询操作O(logN)
修改操作O(len logN)
总复杂度上界O(T * q * len * logN)

*/#include<cstdio>#include<iostream>#include<algorithm>#include<vector>#include<cstring>#include<set>#include<queue>#define LL long long#define P pair<int,int>using namespace std;const int M = 1e5 + 10;const int N = 1e3 + 10;char s[N][N];int n,q;int w[N];int ch[M][26],tot;int has[M * 26],id;int len[N],ran[N],End[N];int sum[N][N];bool cmp(int x,int y){    return len[x] < len[y];}void init(){    id = tot = 0;    memset(ch[0],0,sizeof(ch[0]));    memset(sum,0,sizeof(sum));    memset(has,0,sizeof(has));}int lowbit(int x){    return x & (-x);}void up(int i,int pos,int val){    while(pos <= 1000){        sum[i][pos] += val;        pos += lowbit(pos);    }}int getsum(int i,int pos){    int ans = 0;    while(pos){        ans += sum[i][pos];        pos -= lowbit(pos);    }    return ans;}int ix(char c){    return c - 'a';}void Insert(char *s,int v){    int o = 0,c;    for(int i = len[v] - 1;i >= 0;i--){        c = ix(s[i]);        if(!ch[o][c]){            memset(ch[++tot],0,sizeof(ch[tot]));            ch[o][c] = tot;        }        o = ch[o][c];        if(has[o]) up(has[o],w[v],1);    }    if(!has[o]){        has[o] = ++id;        up(has[o],w[v],1);    }    End[v] = has[o];}void update(int v,char *s,int y){    int o = 0,c;    for(int i = len[v] - 1;i >= 0;i--){        c = ix(s[i]);        o = ch[o][c];        if(has[o]){            up(has[o],w[v],-1);            up(has[o],y,1);        }    }    w[v] = y;}int main(){    int T;    cin>>T;    while(T--){        scanf("%d",&n);        for(int i = 1;i <= n;i++){            scanf("%s%d",s[i],&w[i]);            ran[i] = i;            len[i] = strlen(s[i]);        }        init();        sort(ran+1,ran+n+1,cmp);        for(int i = 1;i <= n;i++){            int v = ran[i];            Insert(s[v],v);        }        scanf("%d",&q);        int op,x,y;        while(q--){            scanf("%d%d",&op,&x);            if(op == 1){                scanf("%d",&y);                update(x,s[x],y);            }else printf("%d\n",getsum(End[x],w[x]));        }    }    return 0;}