20171022模拟赛

字符串(string)

【题目描述】

    给定两个字符串s,t,其中s只包含小写字母以及*,t只包含小写字母。你可以进行任意多次操作,每次选择s中的一个*,将它修改为任意多个(可以是0个)它的前一个字符。询问是否能将s修改为t。

【输入描述】

    第一行输入一个整数T,为数据组数。

    每组数据两行,第一行一个字符串s,第二行一个字符串t。

【输出描述】

    每组数据输出一行,如果能将s修改为t,输出Yes,否则输出No。

【样例】

输入

输出

2

a*

aaaa

a*

ab

Yes

No

【数据范围】

对于字符串a,|a|为a的字符串长度。

对于20%的数据,|s|,|t|<=7。

对于60%的数据,|s|,|t|<=300。

对于100%的数据,T<=100,|s|,|t|<=30000。

 

20%:首先我们发现多个*连在一起与一个*是等价的。我们只要枚举每个*把上一个字母复制了多少遍就行了。

60%:用f[i][j]表示s[1…i]能否变为t[1…j]。如果s[i]不是*那么f[i][j]=f[i-1][j-1],否则f[i][j]=max{f[i-1][k]},其中t[k+1],…,t[j]=s[i-1]。

满足条件的k显然是一个区间,对每个j预处理出k,然后用前缀和优化转移即可。

时间复杂度O(T|s||t|)

PS:当然,前面都是瞎扯

100%:将s和t划分为若干个极长段,满足每段以字母开头且段内只有一种字母。显然这些段是一一对应的,对于s中每一段,如果有*,那么它可以变为字母个数>=它的全字母段。

时间复杂度O(T(|s|+|t|))

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char t[30010],s[30010];
int main()
{
    //freopen("string.in","r",stdin);
    //freopen("string.out","w",stdout);
    int T;scanf("%d",&T);
    int i,j,k,l,n,m,cnt;
    while(T--){
        scanf("%s%s",s,t);
        n=strlen(s);m=strlen(t);
        for(i=j=0;i<n;i=k,j=l){
            cnt=0;
            for(k=i;k<n&&(s[k]=='*'||s[k]==s[i]);k++)
                if(s[k]=='*')cnt++;
            for(l=j;l<m&&t[l]==t[j];l++);
            if(s[i]!=t[j]||k-i-cnt>l-j||(cnt==0&&k-i!=l-j))break;
        }
        if(i<n||j<m)puts("No");
        else puts("Yes");
    }
    return 0;
}
View Code

 

或(or)

【题目描述】

    你需要构造一个长度为n的数列X,当中的数字范围从0到2^30-1。除此之外你需要满足m个条件,第i个条件为X[li]|X[li+1]|……|X[ri]=pi。|为按位或运算。

【输入描述】

    第一行输入两个整数n,m

    接下来的m行每行输入三个整数li,ri,pi

【输出描述】

如果存在这样的序列,第一行输出Yes,否则输出No。

如果存在这样的序列,第二行输出n个数,为数列X。

【样例】

输入

输出

2 1

1 2 1

Yes

1 1

【数据范围】

对于30%的数据,n,m<=1000。

对于另外30%的数据,pi<=1。

对于100%的数据,n,m<=100000,1<=li<=ri<=n,0<=pi<2^30。

 

中间30%:对于每个条件,如果pi=0,那么x[li]…x[ri]显然都为0,否则至少要有一个1。

由于我们只需要构造出一组可行解,那么我们可以把没被要求为0的x[i]都设为1,然后判断是否满足每个条件即可。

区间赋值和区间询问可以用线段树维护。

时间复杂度O(mlogn)

100%:由于或运算每一位是相互独立的,因此可以将上述做法推广到pi>1的情况。

初始时x[i]=2^30-1,对于每个条件,将x[li]…x[ri]中pi=0的位修改为0,最后判断是否满足条件。同样使用线段树维护。

时间复杂度O(mlogn)

PS:代码暂缺

商店(shop)

【题目描述】

    在m天内,每天都有n种商品,第i种的物品的价格为vi,此物品每天最多只能购买xi个。第i天你有wi的钱,你会不停购买能买得起的最贵的物品。你需要求出每天会购买多少个商品。每一天的钱如果有剩,那么将会被Ditoly拿走,而不会被留到第二天用。

【输入描述】

第一行两个整数n,m。

接下来n行每行两个整数vi,xi。接下来m行每行一个整数wi。

【输出描述】

    m行每行一个整数,第i行表示第i天购买的物品数量。

【样例】

输入

输出

3 3

1 1

2 2

3 3

5

10

15

2

4

6

【数据范围】

对于20%的数据,n,m<=1000。

对于另外40%的数据,xi=1。

对于100%的数据,n,m<=100000,1<=vi<=10^9,1<=xi<=10000,0<=wi<=10^18。

 

20%:按题意暴力模拟即可。

时间复杂度O(nm)。

中间40%:首先对物品按价格排序并预处理出前缀和。对于每次询问,我们二分找出能买得起的最贵的物品i,再二分找出能买得起的连续一段物品i~j。

由于买下i~j后买不起j+1,并且j+1的价格不大于i的价格,因此至少花费了一半的钱。那么我们只需要二分logw次即可。

时间复杂度O(nlognlogw)。

100%:不难发现上述做法可以推广到每种物品个数>1的情况,也就是二分能全部买下的一段,再求一下下一个物品能买多少个。

时间复杂度O(nlognlogw)。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
inline int read(){
    int t=1;int num=0;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();}
    while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();}
    return num*t;
}
const int N=100010;
int n,m;
ll V[N],X[N];
struct goods{int v,x;}a[N];
bool cmp(goods a,goods b){return a.v<b.v;}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)a[i].v=read(),a[i].x=read();
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
        V[i]=V[i-1]+(ll)a[i].v*a[i].x,
        X[i]=X[i-1]+a[i].x;
    while(m--){
        ll k,ans=0;scanf("%lld",&k);int newn=n;
        while(1){
            int l=1,r=newn,mid,big,small;
            while(l+1<r){
                mid=(l+r)>>1;
                if(a[mid].v<=k)l=mid;
                else r=mid;
            }big=l;if(a[r].v<=k)big=r;
            l=1;r=big;
            while(l+1<r){
                mid=(l+r)>>1;
                if(V[big]-V[mid]>=k)l=mid;
                else r=mid;
            }small=l;if(V[big]-V[r]<=k)small=r;
            if(big>=small)ans+=X[big]-X[small],k-=V[big]-V[small];
            int tmp=min(k/a[small].v,(ll)a[small].x);
            ans+=tmp;k-=(ll)tmp*a[small].v;newn=small-1;
            if(small==1)break;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

本文由Yzyet编写,网址为www.cnblogs.com/Yzyet。非Yzyet同意,禁止转载,侵权者必究。

转载于:https://www.cnblogs.com/Yzyet/p/7875525.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值