[hihoCoder挑战赛24](贪心)(期望DP)(后缀自动机)

来源:互联网 发布:2016年进出口数据分析 编辑:程序博客网 时间:2024/05/18 01:57

A:Rikka with Sequence IV

题意简述

有一个长度为n的数列A,求一个长度为n的排列P,满足对于任意的i[1,n]都满足:ij=1Apj0
求想要知道满足条件的字典序最小的排列P

思路

贪心。
从前往后扫,选出第一个前缀和大于0的。
这样扫n遍扫出所有的数。
复杂度O(n2)

代码

#include<cstdio>using namespace std;int n,cnt;long long sum;int seq[1010],ans[1010];bool vis[1010];int main(){    scanf("%d",&n);    for (int i=1;i<=n;i++)        scanf("%d",&seq[i]);    for (int i=1;i<=n;i++)    {        for (int j=1;j<=n;j++)            if (!vis[j]&&sum+seq[j]>=0)            {                sum+=seq[j];                ans[++cnt]=j;                vis[j]=1;                break;            }        if (cnt!=i)        {            printf("Impossible");            return 0;        }    }    for (int i=1;i<=n;i++)        printf("%d ",ans[i]);    return 0;}

B:Rikka with Subsequence

题意简述

有一个长度为n的字符串s,现在他对这个字符串进行了一个操作,在操作之后第i个字符有Ai%的概率被删除。现在他想要知道在操作之后得到的新的字符串的本质不同的子序列个数的期望值(包括空串)。
Note:字符串s是字符串t的子序列当且仅当删除了t中若干个位置之后可以得到字符串s

思路

这个人还没有做过
题解见这个人的题解

代码

C:Rikka with String

题意简述

有一个长度为n的只包含小写字母的字符串s,现在可以选取一个位置 k[1,n] 并把s[k]上的字符替换成#
对每一个k[1,n],求修改后的字符串中本质不同的子串个数。

思路

i位置的答案为g[i],初值为不替换的不同子串的个数,可以观察到,如果替换s[i]#,一定会使得答案增加。
考虑每一种本质不同子串对答案的影响。
某一子串的出现位置为(lj,rj),如果ji[lj,rj],那么就会对g[i]贡献1的答案。
我们发现这样太蛋疼了= =
考虑补集转换,对于某一个位置i,它变成了一个没有出现过的字符,我们先把所有可能增加的答案,即为跨过这个点的所有子串都加进去,再减去不合法的。
这样我们继续考虑本质不同子串对答案的影响,如果对于 ji[lj,rj],它对g[i]有-1的贡献。
我们发现这个东西就非常好统计了!
后缀三兄弟皆可搞!
在这里我选用了后缀自动机
我们需要的有四个信息:
1.minx,right集合中最小的位置。
2.maxx,right集合中最大的位置。
3.maxlen,所能接受的最长子串。
4.minlen=premaxlen+1,所能接受的最短子串。
可以通过简单的拓扑求出。
子串的贡献区间为[maxxlen+1,minx]
后缀自动机一个节点表示多个right集合相同的不同长度的子串。
那么一个节点对[maxxlen+1,maxxminlen]贡献一个首项为-1,公比为-1的等差数列;
[maxxminlen+1,minx]贡献maxlenminlen+1
可以用树状数组实现之,时间复杂度O(nlogn)
二阶差分可以得到更优的时间复杂度。具体做法是先对g差分得到g,再对g差分得到g′′,加一个等差数列和区间加一个常数都变为了单点修改。最后做两次前缀和即可得到g
时间复杂度O(n)

涨姿势……

代码

#include<cstdio>#include<iostream>#include<cstring>using namespace std;#define INF 0x3f3f3f3fint n;long long total;long long ans[300010];char st[600010];namespace SAM{    struct Node{        Node *pre,*next[26];        int val,minx,maxx;        Node(){};        Node(int);    }*null;    Node::Node(int _val)    {        val=_val;        for (int i=0;i<26;i++)            next[i]=null;        minx=INF;        maxx=0;        pre=null;    }    Node pool[600010];    Node *Root,*current;    int poolt,now;    int sum[600010],order[600010];    Node *NewNode(int _val)    {        pool[++poolt]=Node(_val);        return &pool[poolt];    }    void Init()    {        null=NewNode(0);        *null=Node(0);        Root=NewNode(0);        current=Root;    }    void extend(char ch)    {        int x=ch-'a';        Node *p=current;        Node *np=NewNode(p->val+1);        while (p!=null&&p->next[x]==null)            p->next[x]=np,p=p->pre;        if (p==null)            np->pre=Root;        else        {            Node *q=p->next[x];            if (q->val==p->val+1)                np->pre=q;            else            {                Node *nq=NewNode(p->val+1);                memcpy(nq->next,q->next,sizeof(q->next));                nq->pre=q->pre;                q->pre=nq;                np->pre=nq;                while (p!=null&&p->next[x]==q)                    p->next[x]=nq,p=p->pre;            }        }        total+=np->val-np->pre->val;        np->maxx=np->val;        np->minx=np->val;        current=np;    }    void solve()    {        for (int i=2;i<=poolt;i++)            sum[pool[i].val]++;        for (int i=1;i<=n;i++)            sum[i]+=sum[i-1];        for (int i=2;i<=poolt;i++)            order[sum[pool[i].val]--]=i;        for (int i=poolt-1;i>=1;i--)        {            pool[order[i]].pre->minx=min(pool[order[i]].pre->minx,pool[order[i]].minx);            pool[order[i]].pre->maxx=max(pool[order[i]].pre->maxx,pool[order[i]].maxx);        }        for (int i=poolt-1;i>=1;i--)            if (pool[order[i]].maxx-pool[order[i]].val+1<=pool[order[i]].minx)            {                int l=pool[order[i]].maxx-pool[order[i]].val+1;                int r=min(pool[order[i]].maxx-(pool[order[i]].pre->val+1)+1,pool[order[i]].minx);                int pp=r-l+1;                if (l<=r)                {                    ans[l]++;                    ans[r+1]-=pp+1;                    ans[r+2]+=pp;                }                l=r+1,r=pool[order[i]].minx;                if (l<=r)                {                    ans[l]+=pp;                    ans[l+1]-=pp;                    ans[r+1]-=pp;                    ans[r+2]+=pp;                }            }        for (int i=1;i<=n;i++)            ans[i]+=ans[i-1];        for (int i=1;i<=n;i++)            ans[i]+=ans[i-1];    }}void init(){    scanf("%d",&n);    scanf("%s",st+1);    SAM::Init();    for (int i=1;i<=n;i++)        SAM::extend(st[i]);}void work(){    SAM::solve();    for (int i=1;i<=n;i++)        printf("%lld ",1LL*i*(n-i+1)+total-ans[i]);}int main(){    init();    work();    return 0;}
0 0