NOI(Pianfen) 模拟赛

D1

普及组。。。

T1/T2
T3
链接:C

小A有一个只包含左右括号的字符串S。但他觉得这个字符串不够美观,因为它不是一个合法的括号串。一个合法的括号串是这样定义的:

  1. ()是合法的括号串

  2. 若A是合法的括号串,则(A)则是合法的括号串

  3. 若A,B是合法的括号串,则AB也是合法的括号串。

小A现在希望删掉S中若干个字符,使得剩下的字符串是一个合法的括号串。小A想知道有多少不同的方案。两个方案是不同的,当且仅当他们删除的位置不同。比如当S是(()时,有两种方案。分别是删掉第一个位置,或是删掉第二个位置。


dp,\(f[i][j]\)表示前\(i\)个位置未配平的左括号的个数为\(j\)的方案数,转移
\[f[i][j]=\left\{\begin{matrix} &s[i]='('\ \ | f[i][j]=f[i-1][j-1]+f[i-1][j]& \\ &s[i]='\ )'\ \ | f[i][j]=f[i-1][j]+f[i-1][j+1] & \end{matrix}\right.\]

标程是滚动数组,然鹅并不需要啊并不需要,直接压成一位数组即可,每次转移时左移或右移一位。

#include<iostream>
#include<cstdio>

using namespace std;

int i,m,n,j,k,a[1001][1001],b[10001],f[10001];
char c;
int main()
{
    scanf("%d",&n);
    for(i=1;i<=n;i++) 
    {
        cin>>c;
        if(c==')') b[i]=1;
    }
    f[0]=1;
    for(i=1;i<=n;i++)
    if(b[i]) for(j=1;j<=k;j++) f[j-1]+=f[j];
    else {
        k+=1; 
        for(j=k;j>=0;j--) f[j+1]+=f[j];
    }
    printf("%d",f[0]-1);
}

T4
链接:D

小A有n个长度都是L的字符串。这些字符串只包含前8个小写字符,'a'~'h'。但这些字符串非常的混乱,它们几乎长得互不相同。小A想通过一些规则,让它们长得尽可能相同。小A现在有K次机会,他可以每次机会,可以选择一对字符x,y,让x,y变成等价的字符(注意这里x,y和字符'x', 'y'不是一样的,只是个代号)。注意,等价关系是有传递性的。比如小A让'a'和'b'等价, 'b'和'c'等价,那么'a'和'c'等价。

对于两个长度字符串P,Q是等价的,当且仅当对于每一位,P的字符和Q的字符都是等价的。

小A希望你告诉他,要怎么利用好这K次机会(当然可以不用完),使得尽可能多对字符串是等价的。注意每对字符串只能算一次。


普及组考斯特林数...我可能真的只适合去玩泥巴

当k大于7时我们可以把图连成一棵树也就是全部匹配。
穷举匹配情况时间爆炸,所以枚举的是联通情况是为(8,8-K),在可以接受的范围内。
由于字符串长度很大,check答案还是会超时,所以Hash掉每个字母的出现情况一起判断即可


D2

T1
链接:A

小N得到了一个非常神奇的序列A。这个序列长度为N,下标从1开始。A的一个子区间对应一个序列,可以由数对[l,r]表示,代表A[l], A[l + 1], ..., A[r]这段数。对于一个序列B[1], B[2], ..., B[k],定义B的中位数如下:

  1. 先对B排序。得到新的序列C。

  2. 假如k是奇数,那么中位数为。假如k为偶数,中位数为

对于A的所有的子区间,小N可以知道它们对应的中位数。现在小N想知道,所有长度>=Len的子区间中,中位数最大可以是多少。


一开始打算打60分的平衡树偏分来着...突然想到似乎可以二分?

由于中位数大小其实并不具有单调性(比如最小数在k>1的情况下无论如何也不会是中位数..)但是可行性还是单调的,每次check一个二分到的值\(x\)时把所有大于等于\(x\)的数全部赋值为1,小于\(x\)的数全部赋值为\(-1\)维护一个前缀和然后穷举每一个区间右端点然后找到长度大于等于k的最小值左端点,若相减只差大于0则最终答案肯定在\(x\)\(x\)以上,否则则在\(x\)以下。

考试时写的愚蠢代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define update(now) c[now]=min(c[now*2],c[now*2+1])
using namespace std;

int i,m,j,k,a[100001],t[100001],pre[100001],maxx,c[500001],d[100001],e[100001],n,g[100001],w[100001];

void built(int now,int l,int r)
{
    if(l==r) {c[now]=e[l]; return ;}
    int mid=(l+r)>>1;
    built(now*2,l,mid);
    built(now*2+1,mid+1,r);
    update(now);
}

int cha(int now,int l,int r,int rr)
{
    int ans=0x3f3f3f3f;
    if(r<=rr) return c[now];
    int mid=(l+r)/2;
    if(mid+1<=rr)  ans=min(ans,cha(now*2+1,mid+1,r,rr));
    ans=min(ans,cha(now*2,l,mid,rr));
    return ans;
}

bool pan(int x)
{
    memset(c,0x3f,sizeof(c));
    memset(g,0,sizeof(g));
    memset(w,0,sizeof(w));
    e[0]=0;
    
    for(int i=1;i<=n;i++) 
    {
        if(a[i]>x) d[i]=1,g[i]=g[i-1]; 
        else if(a[i]==x) d[i]=0, g[i]=i;
        else d[i]=-1,g[i]=g[i-1];
        e[i]=e[i-1]+d[i];
    }
    
    built(1,0,n);
    for(int i=k;i<=n;i++)
        if(e[i]-cha(1,0,n,i-k+1)>0) return 1;
    for(int i=k;i<=n;i++)
    if(g[i]) if(e[i]-cha(1,0,n,min(i-k,g[i]))>=0) return 1;
    return 0;
}


int ef(int l,int r)
{
    while(l<r) 
    {
        int mid=(l+r+1)/2;
        if(mid==36)
        {
            mid=36;
        }
        if(pan(mid)) l=mid;
        else r=mid-1;
    }
    return l;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++) scanf("%d",&a[i]),t[i]=a[i];
    sort(t+1,t+n+1);

    m=unique(t+1,t+1+n)-t-1;
    for(int i=1;i<=n;i++) 
    {
        int z=lower_bound(t+1,t+1+m,a[i])-t;
        pre[z]=a[i];    a[i]=z;
        maxx=max(maxx,a[i]);
    }
    printf("%d",pre[ef(1,maxx)]);
}

然鹅并不用啊,最小值是单调的....
修改后

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

int i,m,j,k,a[100001],maxx,c[500001],d[100001],e[100001],n,tmp;

bool pan(int x)
{
    for(int i=1;i<=n;i++) 
    {
        if(a[i]>=x) e[i]=e[i-1]+1; 
        else e[i]=e[i-1]-1;
    }
    int p=0x3f3f3f3f;
    for(int i=k;i<=n;i++)
    {
        p=min(p,e[i-k]);
        if(e[i]-p>0) return 1;
    }
    return 0;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++) scanf("%d",&a[i]),maxx=max(maxx,a[i]);
    int l=1, r=maxx;
    while(l<=r) 
    {
        int mid=(l+r)/2;
        if(pan(mid)) tmp=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d",tmp);
}

T2
链接:B

小N对于数字的大小一直都有两种看法。第一种看法是,使用字典序的大小(也就是我们常用的判断数字大小的方法,假如比较的数字长度不同,则在较短一个前面补齐前导0,再比较字典序),比如43<355,10<11。第二种看法是,对于一个数字,定义他的权值为,也就是各个数位的乘积。

现在给定两个区间,[L,R]与[L1,R1]。小N现在想知道,有多少使用字典序判大小法在[L,R]之间的数字,满足其第二种定义的权值也在[L1,R1]之间。

换句话说,对于一个数x,定义f(x)为x的各个数位的乘积。对于L<=x<=R,问有多少x满足,L1<=f(x)<=R1。


恶意拉长题目描述差评

45分暴力很好啊QWQ

正解
数位dp,把情况分为两种,包含0和不包含0,然后包含0为至少含有1个0的情况,特判即可。
不包含0
\(L1,R1\)质因数分解,由于每一位都\(\in[x|9\leq x\leq1]\)所以分解出的质因数也只有\(2,3,5,7\)四个且都不会超过60个。然后就是数位dp


T3
链接:https://www.nowcoder.com/acm/contest/164/C
来源:牛客网

小A有一个只包含左右括号的字符串S。但他觉得这个字符串不够美观,因为它不是一个合法的括号串。一个合法的括号串是这样定义的:

  1. ()是合法的括号串

  2. 若A是合法的括号串,则(A)则是合法的括号串

  3. 若A,B是合法的括号串,则AB也是合法的括号串。

小A现在希望删掉S中若干个字符,使得剩下的字符串是一个合法的括号串。小A想知道有多少不同的方案。两个方案是不同的,当且仅当他们删除的位置不同。比如当S是(()时,有两种方案。分别是删掉第一个位置,或是删掉第二个位置。


一看就是万恶的数据结构题...(咕咕咕

猜你喜欢

转载自www.cnblogs.com/ZUTTER/p/9613930.html