清北模拟题4

更正:第三组:不存在相同的字符|str|=26,26<=n<=100

题解:kmp+矩阵乘法(类似 GT算法,只需将 GT算法的代码(ps:GT算法是一道题)进行一下修改)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 10100
#define MAXM 110
#define M 1000000007
using namespace std;
int dp[MAXN][MAXM];
int fail[MAXM];
int trs[MAXN][26];
char str[MAXN];
void xx(int &x,int y)
{
        x+=y;
        if (x>=M) x-=M;
}

int main()
{
        int n;
        while (~scanf("%d%s",&n,str+1))
        {
                memset(dp,0,sizeof(dp));
                int m=strlen(str+1);
                fail[1]=0;
                for (int i=2;i<=m;i++)
                {
                        int p=fail[i-1];
                        while (p && str[p+1]!=str[i])
                                p=fail[p];
                        if (str[p+1]==str[i])
                                fail[i]=p+1;
                }
                memset(trs,-1,sizeof(trs));
                memset(trs[0],0,sizeof(trs[0]));
                for (int i=0;i<m;i++)
                        trs[i][str[i+1]-‘a‘]=i+1;
                for (int i=0;i<=m;i++)
                        for (int j=0;j<26;j++)
                                if (trs[i][j]==-1)
                                        trs[i][j]=trs[fail[i]][j];
                dp[0][0]=1;
                for (int i=0;i<n;i++)
                        for (int j=0;j<m;j++)
                                for (int k=0;k<26;k++)
                                        xx(dp[i+1][trs[j][k]],dp[i][j]);
                long long ans=0;
                long long x=1;
                for (int i=n;i>=0;i--)
                {
                        ans = (ans+x*dp[i][m])%M;
                        if (i)x=x*26%M;
                }
                printf("%d\n",(int)((x-ans)%M+M)%M);
        }
}

比这标程打的,没毛病

更正:输出的顺序保证a<b

更正:输出样例:0 1000000006

题解:类似斐波那契数列,进行一下变形。加上矩阵乘法。(数据很大

#include<cstdio>
#include<iostream>
#define M 1000000007
using namespace std;
long long b[3][3],c[3][3],ans[3][3];
long long n;
void work(long long n)
{
    b[1][1]=ans[1][1]=0;
    b[1][2]=ans[1][2]=1;
    b[2][1]=ans[2][1]=1;
    b[2][2]=ans[2][2]=1;
    while(n)
    {
        if(n&1)
        {
            for(int k=1;k<=2;k++)
              for(int i=1;i<=2;i++)
                for(int j=1;j<=2;j++)
                  c[i][j]=(c[i][j]+(ans[i][k]%M*b[k][j]%M))%M;
            for(int i=1;i<=2;i++)
              for(int j=1;j<=2;j++)
                ans[i][j]=c[i][j],c[i][j]=0;
        }
        for(int k=1;k<=2;k++)
          for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
              c[i][j]=(c[i][j]+(b[i][k]%M*b[k][j]%M))%M;
        for(int i=1;i<=2;i++)
          for(int j=1;j<=2;j++)
            b[i][j]=c[i][j],c[i][j]=0;
        n/=2;
    }
    long long ans1=(ans[1][2])%M,ans2=(ans[1][1]+ans[1][2])%M;
    cout<<min(ans1,ans2)<<" "<<max(ans1,ans2);
}
int main()
{
    cin>>n;
    work(n);
    return 0;
}

参考自zjk的代码

更正:模数1000000007

题解:考试的时候这个题全部爆零。当时以为是二分图匹配的题,结果是树形dp,被题目坑了一把。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define maxn 100010
#define mod 1000000007
#define ll long long
using namespace std;
ll T,P,n,head[maxn],num,f[maxn][2],g[maxn][2],L,R[maxn],l,r[maxn],son[maxn];
struct node
{
    ll v,pre;
}e[maxn*2];
void Add(ll from,ll to)
{
    num++;e[num].v=to;
    e[num].pre=head[from];
    head[from]=num;
}
void xx()
{
    num=0;
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    memset(head,0,sizeof(head));
}
void DP(ll now,ll from)
{
    g[now][0]=1;
    ll mx,sum;
    for(int i=head[now];i;i=e[i].pre)
    {
        ll v=e[i].v;
        if(v==from)continue;
        DP(v,now);
        mx=max(f[v][1],f[v][0]);sum=0;
        if(mx==f[v][1])sum+=g[v][1];
        if(mx==f[v][0])sum+=g[v][0];
        g[now][0]=g[now][0]*sum%mod;
        f[now][0]+=mx;
    }
    L=0;l=1;ll S=0;
    for(int i=head[now];i;i=e[i].pre)
        if(e[i].v!=from)son[++S]=e[i].v;
    R[S+1]=0;
    r[S+1]=1;
    for(int i=S;i>=1;i--)
    {
        ll v=son[i];sum=0;
        mx=max(f[v][1],f[v][0]);
        if(mx==f[v][1])sum+=g[v][1];
        if(mx==f[v][0])sum+=g[v][0];
        R[i]=R[i+1]+mx;
        r[i]=r[i+1]*sum%mod;
    }
    for(int i=1;i<=S;i++)
    {
        ll v=son[i];
        mx=L+f[v][0]+R[i+1]+1;
        if(mx>f[now][1])
        {
            f[now][1]=mx;
            g[now][1]=l*g[v][0]%mod*r[i+1]%mod;
        }
        else if(mx==f[now][1])
            g[now][1]=(g[now][1]+l*g[v][0]%mod*r[i+1]%mod)%mod;
        sum=0;
        mx=max(f[v][1],f[v][0]);
        if(mx==f[v][1])sum+=g[v][1];
        if(mx==f[v][0])sum+=g[v][0];
        l=l*sum%mod;
        L+=mx;
    }
}
int main()
{
    cin>>T>>P;
    while(T--)
    {
        cin>>n;
        ll u,v;
        xx();
        for(int i=1;i<n;i++)
        {
            cin>>u>>v;
            Add(u,v);
            Add(v,u);
        }
        DP(1,0);
        ll sum,mx;
        mx=max(f[1][0],f[1][1]);
        sum=0;
        if(mx==f[1][0])
           sum+=g[1][0],sum%=mod;
        if(mx==f[1][1])
          sum+=g[1][1],sum%=mod;
        if(P==1)  cout<<mx<<endl;
        if(P==2)  cout<<mx<<" "<<sum<<endl;
    }
    return 0;
}

树形dp

时间: 2024-10-12 23:55:30

清北模拟题4的相关文章

清北押题班(1)

清北押题冲刺班Text1 T1 Count 问有几个无序二元组 $ (x ?, ?y) $ 满足 $ xy \equiv 1?(mod?P ) $ , $ 0 \leq x < P?, ?0 \leq y <P $ 解题思路: 你没看错,这是day1的T1,一道赤裸裸的数学题. Subtask 1:枚举 $ O(P^2) $ 个二元组,选出符合条件的,再去重: Subtask 2:可以发现模 $ P $ 意义下,一个数 x 有逆元,当且仅当 $ gcd(x, P) = 1 $ .并且如果 $

2017 国庆清北刷题冲刺班《角谷猜想》

原创建时间:2018-07-04 20:05:44 不错的字符串模拟 题目描述 某个名字末尾是654321的小A同学是个大家眼中公认的学霸(虽然他永远不承认),他对题目的直觉到了一种可怕的地步,一眼看出题目的算法对他而言只是小 Case,他甚至能在看到一个证明的瞬间敏锐地判断出这个证明的真伪. 现在小A同学机缘巧合地看到了角古猜想(即对于\(x\)当它为奇数则\(x=3x+1\),\(x\)为偶数,则\(x=\frac{x}{2}\),一直重复这个步骤,则最终\(x\)会变为\(1\)),在看完

清北模拟 求和

给定项数.公比.模数,求首项为1的等比数列的和 #include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<cmath> #define ll long long using namespace std; ll n,k,p; ll sum,numa,n

9.23——清北模拟赛

T 1 . 回形遍历( ( calc .cpp/c/pas) 时间限制:1 1s s内存 限制: 256MB[问题 描 述]给出一个 n*m 的棋盘,按如下方式遍历,请问(x,y)往后 z 步走到的是哪个格子. [输入]输入文件名为 calc.in.一行,包含五个整数:n,m,x,y,z[输出]输出文件名为 calc.out.输出一行,包含两个整数,表示所在格子的横纵坐标[输入输出样例]calc .in        calc .out4 5 3 0 5     2 4[ 样例解释 ] [数据说

清北刷题冲刺 11-03 a.m

纸牌 后缀数组 巧克力

清北刷题冲刺 11-02 a.m

卖书 写代码 迷宫

清北刷题冲刺 11-02 p.m

函数最值 函数最值2 序列

清北刷题冲刺 11-03 p.m

三向城 #include<iostream> #include<cstdio> using namespace std; int n,x,y; int main(){ freopen("city.in","r",stdin);freopen("city.out","w",stdout); // freopen("Cola.txt","r",stdin); scan

# 清北冬令营真题泛做

清北冬令营真题泛做 前言 这段时间为了准备冬令营把清北冬令营真题都做了一下.更个博回顾一下(免得你们老说我咕咕咕). 先写良心PKU的题再写THU的题, 主要是THU的题和PKU比起来真的毒瘤好多...... PKUWC2018 [PKUWC2018]Minimax 一个比较显然的暴力是归并排序,每次直接前后缀计算答案即可. 为啥不用线段树合并代替归并排序呢? 暴力线段树合并,合并的过程中顺便算一下即可,由于权值区间不交所以复杂度一个\(log\). [PKUWC2018]Slay the Sp