传送门:
http://codeforces.com/contest/1073/problem/A
题目大意:
定义长度为n的字符串是一个多样化的字符串:当且仅当这个字符串没有一种字符数目严格大于n/2
找出字符串s的一个多样化子串
|s|<=1000
分析:
可以直接预处理前缀和数组,暴力 O ( ∣ s ∣ 2 σ ) O(|s|^2\sigma) O(∣s∣2σ)判断是否存在有没有一个多样化的子串
但这题有更简单的做法 考虑如何构造出多样化的子串
ab显然是一个多样化的子串 a不是多样化子串
因此只需要判断s是否有连续两个字符不同,如果有的话,这连续两个不同的字符就是一个多样化子串。如果s的字符全部相同,就不存在多样化子串
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
int n;
char s[1100];
int main(){
scanf("%d%s",&n,s+1);
fo(i,2,n) if (s[i]!=s[i-1]){
printf("YES\n%c%c\n",s[i-1],s[i]);
return 0;
}
printf("NO\n");
return 0;
}"
传送门:
http://codeforces.com/contest/1073/problem/B
题目大意:
有n本编号为1~n的书放在一个栈a里
a[1]是栈顶 a[n]是栈底
然后按照b[1]…b[n]的顺序执行操作b[i]将书放进书包里
执行第i个操作时,假设b[i]=x 如果x还在栈a中 那么将a中x及之前所有的书都放进书包里 否则忽视这个操作
求出每一个操作放了多少本书
分析:
直接模拟 用v[x]表示编号为x的书是否还在栈中 维护+暴力处理即可
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
int n,a[330000],b[330000],ans[330000],now;
bool v[330000];
int main(){
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
fo(i,1,n) scanf("%d",&b[i]);
now=1;//!!!
fo(i,1,n){
if (v[b[i]]) continue;
while (1){
ans[i]++;
v[a[now]]=1;
now++;
if (v[b[i]]) break;
}
}
fo(i,1,n) printf("%d ",ans[i]);
return 0;
}
传送门:
http://codeforces.com/contest/1073/problem/C
题目大意:
给定一个长度为n的四方向序列 机器人会沿着这个序列走动 需要修改一些方向 使得机器人最终能够到达(x,y) 修改时只能将一个方向替换为另一个方向 不能添加/删除
判断是否有可能到达(x,y) 可能的话需要最小化(最后一个修改位置-最前一个修改位置+1)
n<=200000
分析:
显然 机器人最终到达的位置和序列中这些方向的顺序无关 只和不同方向的数量有关
如果需要修改操作最前面的位置在l 最后面的位置在r 那么修改掉[l,r]之间的所有方向 当前代价都不会变化(仍为r-l+1)
也就是说 可以用一个区间[l,r]代表一种决策
[l,r]之间的方向都任意选取 代价均为r-l+1
任意选取的话 机器人可能达到的区域最大 越有可能到达(x,y)
到达(x,y)的必要条件是 |n|与x+y奇偶性相同
因为从(0,0)开始每走一步 |x+y|奇偶性都会变化
如果|x+y|和n奇偶性不等的话 说明无解
显然 可以任意修改方向的区间长度越长(r-l+1越大) 那么修改后可能到达的区域越大 越可能到达(x,y)
因此可以考虑二分
二分判断的时候枚举任选方向的区间的起始位置i即可
如果任选区间是 [i,i+len-1]
在x+y与n奇偶性相同的情况下
[1,i-1]∪ [i+len,n]这些方向如果能够到达(x0,y0) 那么修改[i,i+len-1]的方向就能够到达(x,y)的充分必要条件为
abs(x+y-x0-y0)<=l
abs(x-x0)+abs(y-y0)<=l
这样时间复杂度是O(nlogn)
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
int n,x,y,L,R,mi,a[330000][4],x0,y0;
char s[330000];
inline void cal(int l1,int r1,int l2,int r2){
x0=(a[r2][3]-a[l2-1][3]+a[r1][3]-a[l1-1][3])-(a[r2][2]-a[l2-1][2]+a[r1][2]-a[l1-1][2]);
y0=(a[r2][0]-a[l2-1][0]+a[r1][0]-a[l1-1][0])-(a[r2][1]-a[l2-1][1]+a[r1][1]-a[l1-1][1]);
}
inline bool che(int l){
fo(i,1,n-l+1){
cal(1,i-1,i+l,n);
if (abs(x-x0)+abs(y-y0)<=l) return 1;
}
return 0;
}
int main(){
scanf("%d%s%d%d",&n,s+1,&x,&y);
/*if (n&1!=abs(x+y)&1){
printf("-1\n");
return 0;
}*/
if ((n&1)!=(abs(x+y)&1)){//!!! 注意运算优先级
printf("-1\n");
return 0;
}
fo(i,1,n){
fo(j,0,3) a[i][j]=a[i-1][j];
switch (s[i]){
case 'U':a[i][0]++;break;// 要用break结束当前case
case 'D':a[i][1]++;break;// 要用break结束当前case
case 'L':a[i][2]++;break;// 要用break结束当前case
case 'R':a[i][3]++;break;
}
//fo(j,0,3) printf("%d %d:%d\n",i,j,a[i][j]);
}
cal(1,1,2,n);
//printf("%d %d\n",x0,y0);
if (x0==x&&y0==y){
printf("0\n");
return 0;
}
L=1;R=n;
while (L+2<R){
mi=(L+R)>>1;
if (che(mi)) R=mi-1;else L=mi+1;
}
fo(i,L,n) if (che(i)){
printf("%d\n",i);
return 0;
}
printf("-1\n");
return 0;
}
传送门:
http://codeforces.com/contest/1073/problem/D
题目大意:
有n个物品 每个物品价值为a[i]
一个人最开始金钱为T 按1,2,3…n,1,2,3…n,1,2…n的顺序买东西 如果可以买下当前物品就会立即购买 然后考虑下一个物品
求出最终会买下多少物品
分析:
考虑到所有购买到的物品的序列是存在循环节的
考虑先模拟一次 从1~n走一圈 能买就尽量买
然后统计这一圈购买的物品价值和sum 以及数目num
如果num=0 那么说明再继续考虑下去也还是买不到物品 循环终止
如果num>0 那么再购买第二圈 第三圈… 因为拥有的金钱不会增加 可能购买到的物品一定上第一圈购买物品的子集
事实上 如果走完第一圈后剩下的金钱等于T’=Ksum+rem
那么之后考虑购买K圈物品,买到的物品都与第一圈相同
那么就不需要模拟这k圈的过程
可以直接让T’’=rem ans+=numK
然后T’’
然后再采用同样的构成处理下去…
每次暴力重新计算sum和num的复杂度都是O(n)的
如果需要暴力重新计算很多次的话 会TLE
但是实际上并不会TLE
这是因为这题的数值范围是有限的
T < = 1 0 18 T<=10^{18} T<=1018
a i < = 1 0 9 a_{i}<=10^{9} ai<=109
考虑如何构造一个序列a 以及金钱T
使得暴力重新计算的次数尽可能多
可以让
a = 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128...... a={1,2,4,8,16,32,64,128......} a=1,2,4,8,16,32,64,128......
T = 1 + ( 1 + 2 ) + ( 1 + 2 + 4 ) + ( 1 + 2 + 4 + 8 ) . . . . . . T=1+(1+2)+(1+2+4)+(1+2+4+8)...... T=1+(1+2)+(1+2+4)+(1+2+4+8)......
这样的话重新计算sum和num次数是很大的 是O(n)的
这样总时间复杂度会达到 O ( n 2 ) O(n^2) O(n2)
但这样构造数据的话 a i a_{i} ai和 T T T增长是非常快的 可以计算出达到题目中的数值范围时 n最多只能达到30左右
因此总时间复杂度可以看成是 O ( k n ) O(kn) O(kn)的
无脑暴力做即可
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
int n;
long long T,sum,num,ans,a[300000];
int main(){
scanf("%d%lld",&n,&T);
fo(i,1,n) scanf("%lld",&a[i]);
while (T){
sum=0;num=0;
fo(i,1,n) if (T>=a[i]){
T-=a[i];
sum+=a[i];
ans++;
num++;
}
if (!num) break;//!!!
ans+=T/sum*num;
T%=sum;
}
printf("%lld\n",ans);
return 0;
}
传送门:
http://codeforces.com/contest/1073/problem/E
题目大意:
求出[l,r]中所有出现数位种类数不超过k的数之和
1 ≤ l ≤ r < 1 0 18 , 1 ≤ k ≤ 10 1 \le l \le r < 10^{18}, 1 \le k \le 10 1≤l≤r<1018,1≤k≤10
分析:
数位dp经典题
将[l,r]的计算转化为[1,r]-[1,l-1]的答案计算
假设现在求解的问题是1~n中所有数位种类数不超过k的数之和
f[i][j][0/1/2]表示数位长度为i ‘0’~‘9’是否出现的二进制状态为j比n的前i位小/相等/大的数的个数
g[i][j][0/1/2]表示数位长度为i ‘0’~‘9’是否出现的二进制状态为j比n的前i位小/相等/大的所有数的和(对998244353取模后的和)
然后有状态转移方程
f [ i + 1 ] [ j ′ ] [ c ′ ] + = f [ i ] [ j ] [ c ] f[i+1][j'][c']+=f[i][j][c] f[i+1][j′][c′]+=f[i][j][c]
g [ i + 1 ] [ j ′ ] [ c ′ ] + = ( g [ i ] [ j ] [ c ] ∗ 10 + f [ i ] [ j ] [ c ] ∗ l ) , l = 0..9 g[i+1][j'][c']+=(g[i][j][c]*10+f[i][j][c]*l) ,l=0..9 g[i+1][j′][c′]+=(g[i][j][c]∗10+f[i][j][c]∗l),l=0..9
时间复杂度为 O ( l o g 10 r ∗ 2 k ∗ 30 ) O(log_{10}{r}*2^k*30) O(log10r∗2k∗30)
时间复杂度还可以优化到更小一些
题目加强到求平方和也可以类似dp求解
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
const int p=998244353;
long long l,r,ans;
int k,d[20],dt,bn[1200],nn,tt;
long long f[20][1200][3],g[20][1200][3];
inline void cal(long long n,int ww){
long long sum=0;int cc,jj;
dt=0;
while (n){
d[++dt]=n%10;
n/=10;
}
fo(i,1,dt/2) std::swap(d[i],d[dt-i+1]);
fo(i,1,dt) fo(j,0,1023) fo(c,0,2) f[i][j][c]=g[i][j][c]=0;
fo(l,1,9){
if (l<d[1]) cc=0;
if (l>d[1]) cc=2;
if (l==d[1]) cc=1;
f[1][1<<l][cc]=1;g[1][1<<l][cc]=l;
}
fo(i,1,dt-1)
fo(j,0,1023)
if (bn[j]<=k)
fo(c,0,2)
fo(l,0,9){
cc=c;
if (cc==1){
if (l<d[i+1]) cc=0;
if (l>d[i+1]) cc=2;
}
jj=j|(1<<l);
f[i+1][jj][cc]+=f[i][j][c];
if (f[i+1][jj][cc]>=p) f[i+1][jj][cc]-=p;
g[i+1][jj][cc]=(g[i+1][jj][cc]+g[i][j][c]*10+f[i][j][c]*l)%p;
}
fo(i,1,dt)
fo(j,0,1023)
if (bn[j]<=k)
fo(c,0,2){
if (c==2&&i==dt) continue;
sum+=g[i][j][c];
if (sum>=p) sum-=p;
}
ans+=ww*sum;
}
int main(){
fo(i,0,1023){
nn=i;tt=0;
while (nn){
tt++;
nn-=nn&(-nn);
}
bn[i]=tt;
//printf("%d:%d\n",i,tt);
}
scanf("%I64d%I64d%d",&l,&r,&k);
cal(r,1);
cal(l-1,-1);
ans=(ans%p+p)%p;
printf("%I64d\n",ans);
return 0;
}
待补题
待补题