字符串(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。
Solution:
由于一个'*'和多个'*'用法相同,我们可以理解为a*(a*****)和a或者aaaaa....本质上相同。
发现这个性质后我们可以把s和t分成若干段,每段以字母开头且每段中仅含有一个字母。
然后对于s中的每一段,如果存在'*',那么它就可以变成字母个数>=本段字母段。
这个做法是O(T(s+t))。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 int T; 7 char s[30010]={0},t[30010]={0}; 8 int main(){ 9 freopen("string.in","r",stdin); 10 freopen("string.out","w",stdout); 11 int i=1,j=1,x=1,y=1; 12 scanf("%d",&T); 13 while(T--){ 14 scanf("%s",s+1);scanf("%s",t+1); 15 int lens=strlen(s+1);int lent=strlen(t+1); 16 i=1,j=1,x=1,y=1; 17 int flag=1; 18 while(i<=lens&&j<=lent){ 19 int sum=0;flag=1; 20 if(s[x]!=t[y]) { 21 flag=0; 22 printf("No "); 23 break; 24 } 25 while(s[x]==s[i]||s[i]=='*'){ 26 i++; 27 if(s[i]=='*') sum++; 28 } 29 while(t[y]==t[j]) j++; 30 int t1=i-x-sum; 31 int t2=j-y; 32 x=i;y=j; 33 if((t1==t2&&sum==0)||(sum>0&&t1<=t2)) continue; 34 else{ 35 printf("No "); 36 flag=0; 37 break; 38 } 39 } 40 if(flag==1){ 41 if(i<=lens||j<=lent) printf("No "); 42 else printf("Yes "); 43 } 44 } 45 return 0; 46 }
//std 写的玄学好看
1 #include<iostream> 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<algorithm> 7 using namespace std; 8 int T,n,m; 9 char s[30010],t[30010]; 10 int main(){ 11 freopen("string.in","r",stdin); 12 freopen("string.out","w",stdout); 13 int i,j,k,l,u; 14 scanf("%d",&T); 15 while(T--){ 16 scanf("%s%s",&s,&t); 17 n=strlen(s);m=strlen(t); 18 for(i=j=0;i<n;i=k,j=l){ 19 u=0; 20 for(k=i;k<n && (s[k]=='*' || s[k]==s[i]);k++) if(s[k]=='*') u++; 21 for(l=j;l<m && t[l]==t[j];l++); 22 if(!(s[i]==t[j] && k-i-u<=l-j && (u || k-i==l-j))) break; 23 } 24 if(i<n || j<m) printf("No "); 25 else printf("Yes "); 26 } 27 return 0; 28 }
或(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。
Solution:
中间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)
1 #include<iostream> 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<algorithm> 7 using namespace std; 8 int n,m,f[400010],l[100010],r[100010],x[100010],p; 9 inline void add(int i,int j,int k,int l,int r,int x) 10 { 11 if(l<=j && k<=r) 12 f[i]|=x; 13 else 14 { 15 if(l<=(j+k>>1)) 16 add(i<<1,j,j+k>>1,l,r,x); 17 if(r>(j+k>>1)) 18 add(i<<1|1,(j+k>>1)+1,k,l,r,x); 19 } 20 } 21 inline int sum(int i,int j,int k,int l,int r) 22 { 23 if(l<=j && k<=r) 24 return f[i]; 25 else 26 { 27 int p=(1<<30)-1; 28 if(l<=(j+k>>1)) 29 p&=sum(i<<1,j,j+k>>1,l,r); 30 if(r>(j+k>>1)) 31 p&=sum(i<<1|1,(j+k>>1)+1,k,l,r); 32 return p; 33 } 34 } 35 int main() 36 { 37 freopen("or.in","r",stdin); 38 freopen("or.out","w",stdout); 39 int i,j,k; 40 scanf("%d%d",&n,&m); 41 for(p=1;p<n;p<<=1); 42 for(i=1;i<=m;i++) 43 { 44 scanf("%d%d%d",&l[i],&r[i],&x[i]); 45 x[i]^=(1<<30)-1; 46 add(1,1,p,l[i],r[i],x[i]); 47 } 48 for(i=1;i<p;i++) 49 { 50 f[i<<1]|=f[i]; 51 f[i<<1|1]|=f[i]; 52 } 53 for(i=p-1;i>0;i--) 54 f[i]=f[i<<1]&f[i<<1|1]; 55 for(i=1;i<=m;i++) 56 if(sum(1,1,p,l[i],r[i])!=x[i]) 57 break; 58 if(i<=m) 59 printf("No "); 60 else 61 { 62 printf("Yes "); 63 for(i=1;i<=n;i++) 64 printf("%d ",f[p+i-1]^(1<<30)-1); 65 printf(" "); 66 } 67 return 0; 68 }
商店(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。
Solution:
二分。
我们先对物品价格排序,然后二分出能买最贵的一段,然后前缀和相减即可。 O(nlognlogw)
//官方:
20%:按题意暴力模拟即可。
时间复杂度O(nm)。
中间40%:首先对物品按价格排序并预处理出前缀和。对于每次询问,我们二分找出能买得起的最贵的物品i,再二分找出能买得起的连续一段物品i~j。
由于买下i~j后买不起j+1,并且j+1的价格不大于i的价格,因此至少花费了一半的钱。那么我们只需要二分logw次即可。
时间复杂度O(nlognlogw)。
100%:不难发现上述做法可以推广到每种物品个数>1的情况,也就是二分能全部买下的一段,再求一下下一个物品能买多少个。
时间复杂度O(nlognlogw)。
1 #include<cstdio> 2 #include<algorithm> 3 #define MAXN 100005 4 #define LL long long 5 using namespace std; 6 int n,m,w[MAXN]; 7 int l,r; 8 int i,cnt,summ=0; 9 LL mon=0,monn=0; 10 LL sum[MAXN]; 11 struct data{ 12 int v,x; 13 }a[MAXN]; 14 void erfen_l(){ 15 for(l=1,r=i;l<r;){ 16 int mid=(l+r+1)>>1; 17 if(mon>=a[mid].v) l=mid; 18 else r=mid-1; 19 } 20 } 21 void erfen_r(){ 22 for(l=1,r=i;l<r;){ 23 int mid=(l+r)>>1; 24 if(mon>=sum[i]-sum[mid]) r=mid; 25 else l=mid+1; 26 } 27 } 28 bool cmp(data a,data b){return a.v<b.v;} 29 int main(){ 30 freopen("shop.in","r",stdin); 31 freopen("shop.out","w",stdout); 32 scanf("%d%d",&n,&m); 33 for(i=1;i<=n;i++) scanf("%d%d",&a[i].v,&a[i].x); 34 sort(a+1,a+n+1,cmp); 35 for(i=1;i<=n;i++){ 36 sum[i]=sum[i-1]+(LL)a[i].v*a[i].x; 37 w[i]=w[i-1]+a[i].x; 38 } 39 while(m--){ 40 scanf("%lld",&mon); 41 for(i=n,cnt=0;i>0&&mon>=a[1].v;){ 42 erfen_l(); 43 i=l; 44 if(mon>=sum[i]) {cnt+=w[i];break;} 45 erfen_r(); 46 mon-=sum[i]-sum[l]; //summ=sum[i]-sum[l]; 47 cnt+=w[i]-w[l]+mon/a[l].v;//monn=mon/a[l].v; 48 mon%=a[l].v; 49 i=l-1; 50 } 51 printf("%d ",cnt); 52 } 53 return 0; 54 }