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。

这我也不知道是什么算法了,反正就是随便做,但是就是狗了,究其原因,4个t打成s

首先,我们对于s串预处理,为了使之后便于比较(即将s和t划分为若干个极长段,满足每段以字母开头且段内只有一种字母。)

我们把几个相同的字符(去掉*后的)这么表示,ss[tots]表示该字符,sss[tots]表示该字符出现的次数

对于t串,也进行如上处理,tt[tott]表示该字符,ttt[tott]表示该字符出现的次数

那么*要怎么办呢?我们用数组_s[tots]表示ss[tots]字符是否存在*(即该字符是否能被无限加长)

具体实现就记下上一个搜到的不为*的字符的位置(记字符也是可以的)(记为last),然后把当前字符与last比较,若相等,则sss[tots]++,若不等且该位不是*,则tots++,然后记录下这种字符ss[tots]=s[i],把它的出现次数设为1  sss[tots]=1,把last记为它,若该位是*,则让_s[tots]=1即可(即ss[tots]字符能被无限延长)

若tots!=tott,那么一定无解(相同字符的段数都不相同了,就不可能有解)

否则我们对于ss和tt的每一位匹配,若这一位的字符一样且出现次数相等(或s串出现次数<t串出现次数且s串该字符能被无限延长)则继续下一位,否则无解

注意:数组要清0,strlen的效率是O(len)的,不要放在循环里,在循环开始的时候做一次就好了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s[30101],t[30101];
char ss[30101],tt[30101];int sss[30101],ttt[30101];bool _s[30101];
int main()
{
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%s%s",s,t);memset(sss,0,sizeof(sss));memset(ttt,0,sizeof(ttt));memset(_s,0,sizeof(_s));
        int tots=0,tott=0;
        int now=0,last=30010,lens=strlen(s),lent=strlen(t);s[30010]='&';
        for(int i=0;i<lens;i++)
        {
            if(s[i]=='*')_s[tots]=1;
            else if(s[i]==s[last])sss[tots]++;
            else if(s[i]!=s[last]){tots++;sss[tots]=1;ss[tots]=s[i];last=i;}
        }
    //    for(int i=1;i<=tots;i++)cout<<ss[i]<<" "<<sss[i]<<" "<<_s[i]<<endl;
        now=0;last=30010;t[30010]='&';
        for(int i=0;i<lent;i++)
        {
            if(t[i]==t[last])ttt[tott]++;
            else if(t[i]!=t[last]){tott++;ttt[tott]=1;tt[tott]=t[i];last=i;}
        }
    //    for(int i=1;i<=tott;i++)cout<<tt[i]<<" "<<ttt[i]<<endl;
        int x=1,y=1;bool ok=1;
        for(int i=1;i<=min(tots,tott);i++)
        {
            if(ss[i]==tt[i]&&(sss[i]==ttt[i]||sss[i]<=ttt[i]&&_s[i]));
            else {ok=0;break;}
        }
        if(ok==0||tots!=tott)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。

商店(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。

各种二分。其实我也不能保证复杂度,但是是我能想到的最快的算法了。

显然,我们应该先按vi排序,处理出v[i]*x[i]的前缀和(为了计算价钱)和x[i]的前缀和(为了计算答案)

由于我们要从能买的起的最贵的开始买,我们先二分找出能买的最贵的物品i,然后再二分找出能买得起的连续一段物品i~j。然后为了提高效率,看看j+1这个物品能买多少件,然后尽可能买,扣除相应的钱,继续循环,直到任何一件东西都买不起为止

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long sum[100100],sumx[100100];
struct xxx{
    long long v,x;
}d[100100];
int search1(int l,int r,long long y)
{
    while(l<r)
    {
        int mid=(l+r)/2;
        if(d[mid].v<=y)r=mid;
        else l=mid+1;
    }
    return l;
}
int search2(int l,int r,long long y,int last)
{
    while(l<r)
    {
        int mid=(l+r)/2;
        if(sum[mid]-sum[last]>y)r=mid;
        else l=mid+1;
    }
    return l-1;
}
bool cmp(xxx a,xxx b){return a.v>b.v;}
int main()
{
//    freopen("shop.in","r",stdin);freopen("shop.out","w",stdout);
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&d[i].v,&d[i].x);
    sort(d+1,d+n+1,cmp);
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+d[i].v*d[i].x,sumx[i]=sumx[i-1]+d[i].x;
    for(int i=1;i<=m;i++)
    {
        long long y;scanf("%lld",&y);
        long long ans=0;
        while(1)
        {
            int ii=search1(1,n+1,y);int jj=search2(ii,n+1,y,ii-1);//cout<<ii<<" "<<jj<<endl;
            if(jj>=ii){ans+=sumx[jj]-sumx[ii-1];y-=sum[jj]-sum[ii-1];}
            if(jj==n)break;
            ans+=(y/d[++jj].v);y=y%d[jj].v;//cout<<y<<"@@"<<ans<<endl;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2017-10-23 20:31  lher  阅读(294)  评论(0编辑  收藏  举报