写之前,先发表下感慨:好久没写题解了,也许是因为自己越来越急利了,也可以说是因为越来越懒了。
A. Diverse Substring
直接找一找有没有相邻的两个不同的字符即可。
B. Vasya and Books
分别记录书本摆放顺序$a[]$,取书顺序$b[]$,每本书的位置$pos[]$,每本书在不在书堆上$in[]$,书堆最顶端的书的位置$top$(因为用的是数组,元素位置是固定的)。
然后,按照题意枚举输入的取书顺序$b[]$,同时记录答案$ans$,更新$top$即可。
C. Vasya and Robot
过滤掉目的地坐标累加和的绝对值和指令长度奇偶性不相同的情况。然后,剩余的所有情况,要么指令步数不够,要么一定有解。此时,答案满足单调性(如果不提前过滤掉奇偶性不同的情况,答案不满足单调性),二分答案即可。具体做法是,预处理出$x$轴方向的前缀和(向右$+1$,向左$-1$)和后缀和,以及$y$轴方向的前缀和(向上$+1$,向下$-1$)和后缀和,二分答案时,直接枚举区间左端点,然后$O(1)$检查枚举区间之外的$x$轴方向和$y$轴方向所需要改变的步数是不是小于二分枚举的区间长度。注意,这个地方只能用$\le$,不能用$==$,否则,会错在这样的样例上:
7 RRRRRRR 3 0
具体代码如下(附个人自用的测试样例):
#include<bits/stdc++.h> using namespace std; int n; char s[200010]; int dx,dy; int prex[200010],postx[200010]; int prey[200010],posty[200010];bool can(int len){for(int i=1;i+len-1<=n;++i){int needx=abs(dx-(prex[i-1]+postx[i+len]));int needy=abs(dy-(prey[i-1]+posty[i+len]));if(needx+needy==len){return 1;}}return 0; }int main() {int cntx=0,cnty=0;scanf("%d%s%d%d",&n,s+1,&dx,&dy);for(int i=1;i<=n;++i){prex[i]=prex[i-1],prey[i]=prey[i-1];if(s[i]=='L')prex[i]--,cntx--;else if(s[i]=='R')prex[i]++,cntx++;else if(s[i]=='D')prey[i]--,cnty--;else if(s[i]=='U')prey[i]++,cnty++;}for(int i=n;i>=1;--i){postx[i]=postx[i+1],posty[i]=posty[i+1];if(s[i]=='L')postx[i]--;else if(s[i]=='R')postx[i]++;else if(s[i]=='D')posty[i]--;else if(s[i]=='U')posty[i]++;}if((abs(dx)+abs(dy))%2!=(abs(cntx)+abs(cnty))%2)return puts("-1")&0;int low=-1,high=n+1,mid;while(low<high-1){mid=(low+high)/2;if(can(mid))high=mid;else low=mid;}if(high<n+1)printf("%d\n",high);else puts("-1");return 0; } /* 7 RRRRRRR 3 0 7 LRLRLRR 3 2 3 UUU 0 2 7 RRRUUUU 1 5 6 UUUUUU 0 6 5 DDDDD 0 1 7 RRRUUUL 7 0 4 URDL 0 0 3 DDD 0 0 2 RR 0 016 UUUUUUUUUUUUUUUU 0 15 2 LD -1 -1 5 RURUU -2 3 6 RRRUUU 2 -2**/
D. Berland Fair
①先考虑$T$大于序列和的情况:此时,$ans+=\frac{T}{序列和}*序列长度$,同时$T%=序列和$;
②当$T$小于序列和时,枚举序列的所有元素,如果$a[i]\le T$,那么$T-=a[i],ans+=1$,并且把$a[i]$放到一个临时序列中,同时记录这些$a[i]$的和,枚举原序列结束后,用临时序列替换原序列,回到步骤①。当临时序列为空时,说明没有$a[i]\le T$,那么,退出循环,输出答案。
#include<bits/stdc++.h> using namespace std; typedef long long LL; #define pb(x) push_back(x) LL n,a[200010]; int main() {LL tot,sum=0,ans=0;vector<LL> v,tmp;scanf("%lld%lld",&n,&tot);for(int i=1;i<=n;++i)scanf("%lld",a+i),v.pb(a[i]),sum+=a[i];while(1){if(tot>=sum){ans+=(tot/sum)*(LL)v.size();tot%=sum;}tmp.clear();sum=0;for(int i=0;i<v.size();++i){if(v[i]<=tot){tot-=v[i];sum+=v[i];ans++;tmp.pb(v[i]);}}if(tmp.size()==0)break;v.assign(tmp.begin(),tmp.end());}printf("%lld\n",ans);return 0; } /* 3 1000000000000000000 1000000000 1000000000 1000000000 7 1000000000000000000 1000000000 1000000000 1000000000 1000000000 1000000000 1000000000 1000000000 6 1000000000000000000 1 1000000000 1 1000000000 1000000000 1000000000 **/
E. Segment Sum
裸的数位$dp$。$dp[pos][sum][bit]$表示前面的数位累加和为$sum$,前面使用过的数字的二进制表示为$bit$时,从$pos$位开始往下搜索,后面的数位随意组合,可以搜索到的满足条件的数字个数以及他们的和。$dp$数组用$pair<long\ long,long\ long>$表示。注意,$dp[pos][sum][bit].second$是表示从$pos$位开始往下搜索,搜到的满足条件的数字的后$pos$位的和。前面的权值部分不算,否则就乱套了。另外,是和,也就是权值和,相当于取后半截作为一个数字的值,而不是数位和。
#include<bits/stdc++.h> using namespace std; #define pb(x) push_back(x) #define mk(x, y) make_pair(x, y) typedef long long LL; typedef pair<LL,LL> pll; LL x,y,k; const LL mod=998244353; int digit[30],dn; pll dp[25][200][1030]; LL pow10[30]; pll dfs(int pos,int sum,int bit,bool limit,bool lead){if(pos<=0)return __builtin_popcount(bit)<=k?mk(1LL,0LL):mk(0LL,0LL);if(!limit&&!lead&&dp[pos][sum][bit].first!=-1)return dp[pos][sum][bit];pll ans=mk(0,0);for(int i=0,top=limit?digit[pos]:9;i<=top;++i){pll pp=dfs(pos-1,sum+i,lead&&i==0?bit:bit|(1<<i),limit&&i==top,lead&&i==0);ans.first+=pp.first%mod;ans.second+=(i*pow10[pos-1]%mod*pp.first%mod+pp.second)%mod;if(ans.second>=mod)ans.second%=mod;}if(!limit)dp[pos][sum][bit]=ans;return ans; }LL solve(LL v){if(v==0)return 0;dn=0;while(v>0){digit[++dn]=v%10;v/=10;}return dfs(dn,0,0,1,1).second; }int main() {pow10[0]=1LL;for(int i=1;i<=19;++i)pow10[i]=pow10[i-1]*10LL;memset(dp,-1,sizeof(dp));scanf("%lld%lld%lld",&x,&y,&k);printf("%lld\n",(mod+solve(y)-solve(x-1))%mod);return 0; } /* 1 100000000 9 **/
F. Choosing Two Paths
G. Yet Another LCP Problem