字符串(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; }
或(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; }
本文由Yzyet编写,网址为www.cnblogs.com/Yzyet。非Yzyet同意,禁止转载,侵权者必究。