牛客网暑期ACM多校训练营(第九场)+训练日记

今天的是欢乐场!大佬们看着我们受折磨而欢乐的欢乐场,,大佬说今天这场的题大多是经典题改编的,感觉很有意思,很多能看的题,,赶脚这套题可以好好研究研究,都比较经典。、

我们今天就A了一个题E,用的概率的知识点的思想。

E、Music Game

题意:n个位置,点或不点(1 or 0),每种情况没有连续x个位置都点了,即得价值X^m,求所有的期望

思路:概率上,其期望是概率*对应价值之和,本题可以得出对于所有情况,

对于每个区间【l,r】考虑,若区间[l,r]内均为1,且l-1,r+1为0,所有包含此区间情况的情况,都有通项((1-P(l-1))*P(l)*……*P(r)*(1-P(r+1)))*((r-l+1)^m),所以可提取出来,剩下的部分任意排列都符合,概率为1,所以整体的期望就是所有区间符合只此区间连续的期望和,,然后取逆元直接再输入P后直接处理就可,,

感想:简直要被这个题气死,做的时候预处理了很多,最后都没用上,,下面代码是删除无用的后,,再就是中间一直调不出来!,尝试输出又因为受逆元影响而无法辨别!!!然后因为自己粗心,将F【1】(表意:1^m,)写成了m~!!!!!真的是要剁手的节奏!!磨蹭了巨多时间!!!全因为这个1!!!这个粗心真的是要不得哇!!!!太坑了!

代码:

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define INV 570000004
long long sum[1005],n,m,F[1005],P[1005],ga[1005][1005];
long long AA(long long a,long long b)
{
    if(b==1) return a%mod;
    if(b%2) return (AA((a*a)%mod,b/2)%mod*a)%mod;
    else return AA((a*a)%mod,b/2)%mod;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    F[1]=1;
    for(int i=2;i<=n;i++)
    {
        F[i]=AA(i,m);
        F[i]%=mod;
    }
    for(int i=1;i<=n;i++)scanf("%lld",&P[i]),P[i]=(P[i]*INV)%mod,ga[i][i]=P[i];;
    long long r,ans=0,z;
    for(int l=2;l<=n;l++)
    {
        for(int i=1;i+l-1<=n;i++)
        {
            r=l+i-1;
            ga[i][r]=ga[i][r-1]*ga[r][r];
            ga[i][r]%=mod;
        }
    }
    for(int l=1;l<=n;l++)
    for(int i=1;i+l-1<=n;i++)
    {
        r=l+i-1;
        if(i-1>=1) z=(1-P[i-1]+mod)%mod;
        else z=1;
        if(r+1<=n) z*=(1-P[r+1]+mod)%mod;
        else z*=1;
        z%=mod;
        ans+=((((ga[i][r]*z)%mod)*F[l])%mod)%mod;
        ans%=mod;
    }
    printf("%lld\n",ans);
}

F、Typing practice

题意:给n个固定串,输入一系列操作,求每次后缀距离固定串的最小距离

思路:晚上看的直播,大佬说是数据出问题了,本来想将kmp,拓展kmp,AC自动机等全部卡掉的,但是数据没卡掉~,正解是trie图。。两眼一抹黑,,完全状况外,,但是比赛的时候这些方法都能过,,我们想的也是kmp,,但是没有调出来,,等着还是看看正解吧

代码:(PS:自己做的KMP的方法的,trie图的因为没学过,就看了看知识点,了解了一下,,也不做了,反正这个题的数据能过~trie图的就跟这个代码差个常数~)

#include<bits/stdc++.h>
using namespace std;
int nextt[5][100005],ans[100005],len[5];
void getFail(char *P,int *f)//P模板串,f是next数组
{
    f[1]=f[0]=0;
    int m=strlen(P);
    for(int i=1,j;i<m;i++)
    {
        j=f[i];
        while(j&&P[i]!=P[j])
            j=f[j];
        f[i+1]=P[i]==P[j]?j+1:0;
    }
    for(int i=1;i<m;i++)
        if(P[i+1]==P[f[i+1]])
            f[i+1]=f[f[i+1]];

            return;
}

/*void find(char *T,char *P,int *f)//P模板串
{
    int n=strlen(T);
    int m=strlen(P);
    int j=0;
    for(int i=0;i<n;i++)
    {
        while(j && T[i]!=P[j]) j=f[j];
        if(T[i]==P[j]) j++;
        if(j==m) cnt++;//i-m+1: 下标从0开始,能够匹配的初始位置。
    }
}*/
char ch[5][100005],chh[100005];
int j[5];
int main()
{
    int n;
    memset(ans,0x3f3f3f3f,sizeof(ans));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch[i]);
        len[i]=strlen(ch[i]);
        ans[0]=min(ans[0],len[i]);
        getFail(ch[i],nextt[i]);
    }
    scanf("%s",chh);
    printf("%d\n",ans[0]);
    int tt=0,p[5][100005];
    for(int i=0;i<strlen(chh);i++)
    {
        if(chh[i]!='-')
        {
            for(int l=1;l<=n;l++)
            {
                while(j[l] && chh[i]!=ch[l][j[l]]) j[l]=nextt[l][j[l]];
                if(chh[i]==ch[l][j[l]]) j[l]++;

                //cout<<l<<" "<<j[l]<<endl;
                p[l][tt]=j[l];
                ans[i]=min(ans[i],len[l]-j[l]);
                //if(j[l]==m) cnt++;//i-m+1: 下标从0开始,能够匹配的初始位置。
            }
            tt++;
        }
        else
        {
            tt--;
            for(int l=1;l<=n;l++)
            {
                j[l]=p[l][tt-1];
                ans[i]=min(ans[i],len[l]-j[l]);
            }
        }
        printf("%d\n",ans[i]);
    }
}

下面是AC自动机 的代码,是很优化的O(n*L+m)的复杂度,跟trie图的复杂度一样,偷的别人的~但是据说思路很好,但是我没有学过这些东西~看不太懂,暂时不看,以后如果还记得的话再看~

//AC自动机
//查询模式串出现次数
#include <bits/stdc++.h>
using namespace std;
queue<int>que;
int ans[100003];
vector<int>g[100003];
struct Trie{
    int ch[100003][26];
    int fail[100003];
    int end[100003];
    int sz;
    int newnode(){
        for(int i=0;i<26;i++)ch[sz][i]=-1;
        end[sz]=0;
        return sz++;
    }
    void init(){
        memset(end,0,sizeof(end));
        sz=0;
        newnode();
    }
    int idx(char c){
        return c-'a';
    }
    void insert(char s[]){
        int len=strlen(s);
        int u=0;
        for(int i=0;i<len;i++){
            int c=idx(s[i]);
            if(ch[u][c]==-1){
                ch[u][c]=newnode();
            }
            u=ch[u][c];
        }
        end[u]=1;
        //que.push(u);
        //ans[u]=1;
    }
    void build(){
        queue<int>q;
        fail[0]=0;
        for(int i=0;i<26;i++){
            if(ch[0][i]==-1)ch[0][i]=0;
            else {
                fail[ch[0][i]]=0;
                q.push(ch[0][i]);
            }
        }
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=0;i<26;i++){
                if(ch[u][i]==-1){
                    ch[u][i]=ch[fail[u]][i];
                }
                else {
                    fail[ch[u][i]]=ch[fail[u]][i];
                    q.push(ch[u][i]);
                }
            }
        }
        for(int i=0;i<sz;i++){
            if(end[fail[i]])end[i]=1;
            //cout<<i<<' '<<fail[i]<<' '<<end[i]<<endl;
            if(end[i]){
                que.push(i);
                ans[i]=1;
            }
        }
    }
    void buildr(){
        for(int i=0;i<sz;i++){
            for(int j=0;j<26;j++){
                g[ch[i][j]].push_back(i);
            }
        }
    }
};

Trie T;

char s[100003];
int rec[100003];
int len=0;

int main(){
    while(!que.empty())que.pop();
    T.init();
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",s);
        T.insert(s);
    }
    T.build();
    T.buildr();
    while(!que.empty()){
        int u=que.front();
        que.pop();
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i];
            if(ans[v])continue;
            ans[v]=ans[u]+1;
            que.push(v);
        }
    }
    scanf("%s",s);
    printf("%d\n",ans[rec[len]]-1);
    for(int i=0;s[i];i++){
        if(s[i]=='-'){
            if(len){
                len--;
            }
            printf("%d\n",ans[rec[len]]-1);
        }
        else {
            int c=s[i]-'a';
            int v=T.ch[rec[len]][c];
            rec[++len]=v;
            printf("%d\n",ans[rec[len]]-1);
        }
        //cout<<rec[len]<<endl;
    }
}
8

H、Prefix Sum

题意:给定n,m,k,两种操作:

1 x y:a[0][x]+=y;

2 x : cout<<a[k][x]<<endl;

a[i][1] = a[i-1][1] and a[i][j] = a[i][j-1] + a[i-1][j] (j >= 2)

思路:据大佬讲,推出公式,,然后两种暴力结合,,构成m√n的复杂度。。

/*但是比赛的时候只推出公式。,,没想到为什么要化成更小的,还可能出现负数。。。有点惊呆,,明天在看,,*/

 

今天看了一下题解,真是优秀!!比赛时我们思路是对的,也一直在想办法维护组合数的和,当然无果~~完全没想到将组合数拆分出来维护、、然后代码也就粘别人的了,,树状数组不想敲了!

代码:

#include <algorithm>
#include  <iostream>
#include   <cstring>
#include   <cstdlib>
#include    <cstdio>
#include    <string>
#include    <bitset>
#include     <cmath>
#include     <queue>
#include     <stack>
#include       <map>
#include       <set>
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps = 1e-10;
const int maxn = 1e5 + 7;
#define REP(i, j, k) for(int i = j;i < k; ++i)
#define PER(i, j, k) for(int i = k - 1;i >= j; --i)
int n, m, k;
ll tr[105][maxn], frac[maxn], i_frac[maxn], inv[105];
void add(int id, int x, ll val)
{
    while(x <= n)
    {
        tr[id][x] = (tr[id][x] + val) % mod;
        x += (x & -x);
    }
}
ll ask(int id, int x)
{
    ll res = 0;
    while(x > 0)
    {
        res = (res + tr[id][x]) % mod;
        x -= (x & -x);
    }
    return res;
}
void init()
{
    inv[1] = 1;
    REP(i, 2, 105) inv[i] = inv[mod % i] * (mod - mod / i) % mod;
}
int main()
{
#ifdef DEBUG
    freopen("input.txt", "r", stdin);
#endif // DEBUG
    scanf("%d %d %d", &n, &m, &k);
    k--;
    init();
    while(m--)
    {
        int op, x, y;
        scanf("%d %d", &op, &x);
        if(op == 0)
        {
            scanf("%d", &y);
            ll u = 1;
            REP(j, 0, k + 1)
            {
                add(j, x, u * y % mod);
                u = u * (k - x - j) % mod * inv[j + 1] % mod;
            }
        }
        else
        {
            ll ans = 0;
            ll u = 1;
            REP(j, 0, k + 1)
            {
                ans = (ans + u * ask(k - j, x) % mod) % mod;
                u = u * (x - j) % mod * inv[j + 1] % mod;
            }
            printf("%lld\n", (ans + mod) % mod);
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值