【NOIP模拟8.4】

T1 打工

PS:本人比较懒就不发原题了(orz),网上查就有
想到了是DP但是转移有一点问题(雾…),然后只好特判+爆搜水过20分

题解:

30分做法
首先我们发现得到的序列有一个性质,即a[i]<=max(a[j])+1 (j<i)
根据这个性质我们可以暴力地枚举每种方案,判断给出的序列属于哪个方案
50分做法
30分做法+特判两个特殊的点(递推)
70分做法
记忆化搜索(同100分做法)
100分做法
f[i][j]表示i待选时,前i-1个数的最大值为j时的方案数
易得f[i][j]=f[i1][j]×j+f[i1][j1]
枚举i的同时维护一个前缀最大值max,将与a的前i1位严格相同,第i位为1~a[i]1的答案累加到f[i][max]即可,答案就是ni=1f[n][i]
考虑到空间可能爆,滚动数组优化即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;

const int N=10010,mod=1000007;
LL f[2][N];
int maxn,n,a[N];
LL ans;
int main()
{
//  freopen("maou.in","r",stdin);
//  freopen("maou.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    maxn=a[1];
    bool u=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
            f[u][j]=(f[!u][j]*j+f[!u][j-1])%mod;
        f[u][maxn]=(f[u][maxn]+a[i]-1)%mod;
//      for(int k=1;k<=i;k++) cout<<f[u][k]<<" ";
//      cout<<endl;
        maxn=max(maxn,a[i]);
        u=!u;       
    }
    for(int i=1;i<=n;i++)
        ans=(ans+f[!u][i])%mod;
    printf("%lld",ans+1);
    return 0;   
}

T2 破解

30分做法
暴力枚举选择哪些区间,哈希判重
100分做法
考虑一种特殊情况,如果所有的区间都不相互覆盖,那么设有m个区间,可以得到的串数目就是2m
对于每一个区间[L,R],连一条LR+1的边(手玩一下就知道为什么了)
那么这样一直加边,如果构成一个联通块(大小为size),那么通过这个连通块的任意size1条边代表的区间都能组成第size条边代表的区间,那么这个区间其实是没有用的,而且从这个连通块中任意选若干条边都不会构成重复的区间,所以这个连通块对答案的贡献就是2size1,最后的答案就应该2mcnt,其中cnt为连通块个数
维护连通块的话并查集就好了,可以将询问的区间离散化一下,因为L,R都比较小不离散化也可以,注意一些细节就好

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int mod=1000000007;
int fa[10000005];
int n,ans,m;

int find(int x)
{
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];   
}

int main()
{
//  freopen("steins.in","r",stdin);
//  freopen("steins.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n+1;i++) fa[i]=i;//!!
        ans=1;
        for(int i=1,l,r;i<=m;i++)
        {
            scanf("%d%d",&l,&r);
            int r1=find(l),r2=find(r+1);
            if(r1!=r2) fa[r1]=r2,ans=(ans<<1)%mod;      
        }
        printf("%d\n",ans);
    }
    return 0;
}

T3 书稿

题解:
待更(本人现在还不会QAQ)……
各种二维差分的差分的前缀和的前缀和的前缀和乱搞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值