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; }
或(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; }