传送门
A题
传送门
题意简述:问你能不能把一个数字串切成若干块,使得切出来的
k
k
k个数
k
≤
2
k\le2
k≤2满足
a
1
<
a
2
<
.
.
.
<
a
k
a_1<a_2<...<a_k
a1<a2<...<ak
思路:模拟。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n;
char s[500];
int main(){
for(ri tt=read();tt;--tt){
n=read(),scanf("%s",s+1);
if(n==2&&s[1]>=s[2]){puts("NO");continue;}
puts("YES"),puts("2");
cout<<s[1]<<' ';
for(ri i=2;i<=n;++i)cout<<s[i];
puts("");
}
return 0;
}
B题
传送门
题意简述:
问第
k
k
k大数根为
i
i
i的数是多少
0
≤
i
≤
9
0\le i\le9
0≤i≤9
思路:一个数的数根等于它
m
o
d
  
9
\mod9
mod9的值。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline ll read(){
ll ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=400;
int n,x;
ll k;
int main(){
for(ri tt=read();tt;--tt){
k=read(),x=read();
cout<<(k-1)*9+x<<'\n';
}
return 0;
}
C题
传送门
题意简述:给
n
n
n个数和一个值
k
k
k,有一个
26
26
26个字符的键盘,每个键不能连续按超过
k
k
k次,现在给你一个按键序列和对于每次按键可以得到的权值,问最后可以得到的总权值是多少(注意如果按了
k
k
k个
a
a
a后按一个
b
b
b的话又可以再按
k
k
k次
a
a
a)。
思路:
我们把相同数字段全部提出来,分别求出前
k
k
k大加起来即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=2e5+5;
int n,k,a[N];
char s[N];
priority_queue<int>q;
ll ans=0;
inline void solve(int l,int r){
while(!q.empty())q.pop();
for(ri i=l;i<=r;++i)q.push(a[i]);
for(ri i=1;i<=k;++i){
if(q.empty())break;
ans+=q.top(),q.pop();
}
}
int main(){
n=read(),k=read();
for(ri i=1;i<=n;++i)a[i]=read();
scanf("%s",s+1);
ans=0;
for(ri l=1,r=1;l<=n;l=r+1,r=l){
while(r<=n&&s[r]==s[r+1])++r;
solve(l,r);
}
cout<<ans;
return 0;
}
D题
传送门
题意简述:有一个
n
∗
n
n*n
n∗n的
01
01
01矩阵
a
a
a,问你能不能构造出一个
k
∗
k
,
满
足
k
∣
n
k*k,满足k|n
k∗k,满足k∣n的矩阵
b
b
b使得
a
i
,
j
=
b
⌊
i
k
⌋
,
⌊
j
k
⌋
a_{i,j}=b_{\lfloor{\frac ik}\rfloor,\lfloor{\frac jk}\rfloor}
ai,j=b⌊ki⌋,⌊kj⌋
思路:暴力枚举
i
i
i,利用前缀和优化
c
h
e
c
k
check
check即可
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=5205;
int n,sum[N][N];
char s[N];
int main(){
n=read();
for(ri i=1;i<=n;++i){
scanf("%s",s+1);
for(ri j=1,v;j<=n;j+=4){
if(s[(j+3)/4]>='A'&&s[(j+3)/4]<='Z')v=s[(j+3)/4]-'A'+10;
else v=s[(j+3)/4]-'0';
sum[i][j]=(v>>3)&1;
sum[i][j+1]=(v>>2)&1;
sum[i][j+2]=(v>>1)&1;
sum[i][j+3]=v&1;
}
}
for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+sum[i][j];
for(ri i=n;i;--i){
if(n!=n/i*i)continue;
bool f=1;
for(ri x=1;x*i<=n;++x){
for(ri y=1,v;y*i<=n;++y){
v=sum[i*x][i*y]-sum[i*x][i*(y-1)]-sum[i*(x-1)][i*y]+sum[i*(x-1)][i*(y-1)];
f&=v==0||v==i*i;
}
}
if(f)return cout<<i,0;
}
return 0;
}
E题
传送门
题意简述:
给出一个
01
01
01串,每次可以选择消去连续的一段,贡献是
a
l
e
n
,
l
e
n
a_{len},len
alen,len表示消去长度,
a
i
a_i
ai会给出,问消去整个串的最大贡献。
思路:
先
d
p
dp
dp出真·
a
i
a_i
ai(既考虑
a
i
a_i
ai和
a
j
+
a
i
−
j
a_j+a_{i-j}
aj+ai−j哪个大,如果后者大就更新前者)。
然后可以区间
d
p
dp
dp,
f
l
,
r
f_{l,r}
fl,r表示区间
[
l
,
r
]
[l,r]
[l,r]的贡献,
g
l
,
r
,
k
,
0
/
1
g_{l,r,k,0/1}
gl,r,k,0/1表示区间
[
l
,
r
]
[l,r]
[l,r]消得只剩下
k
k
k个连续的
0
/
1
0/1
0/1的最大贡献,枚举转移即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const int N=105;
int n,a[N],sum[N][N];
ll f[N],g[N][N][2];
char s[N];
inline void update(ll&a,ll b){a=b>a?b:a;}
int main(){
memset(g,-0x3f,sizeof(g));
n=read(),scanf("%s",s+1);
for(ri i=1;i<=n;++i){
a[i]=f[i]=read();
for(ri j=1;j<i;++j)f[i]=max(f[i],f[j]+f[i-j]);
}
int cnt0=0,cnt1=0;
for(ri i=1;i<=n;++i)sum[1][i]=sum[1][i-1]+(s[i]=='1'),g[i][i][s[i]-'0']=f[1];
for(ri l=1;l<=n;++l)for(ri r=l;r<=n;++r)sum[l][r]=sum[1][r]-sum[1][l-1];
for(ri len=2;len<=n;++len){
for(ri l=1,r=l+len-1;r<=n;++l,++r){
if(s[l+1]=='0')update(g[l][r][0],g[l+1][r][0]+f[r-l+1-sum[l][r]]-f[r-l-sum[l][r]]);
if(s[r-1]=='0')update(g[l][r][0],g[l][r-1][0]+f[r-l+1-sum[l][r]]-f[r-l-sum[l][r]]);
if(s[l+1]=='1')update(g[l][r][1],g[l+1][r][1]+f[sum[l][r]]-f[sum[l][r]-1]);
if(s[r-1]=='1')update(g[l][r][1],g[l][r-1][1]+f[sum[l][r]]-f[sum[l][r]-1]);
for(ri k=l;k<r;++k){
update(g[l][r][0],g[l][k][0]+g[k+1][r][0]);
update(g[l][r][0],g[l][k][0]+g[k+1][r][1]);
update(g[l][r][0],g[l][k][1]+g[k+1][r][0]);
update(g[l][r][1],g[l][k][0]+g[k+1][r][1]);
update(g[l][r][1],g[l][k][1]+g[k+1][r][0]);
update(g[l][r][1],g[l][k][1]+g[k+1][r][1]);
}
}
}
cout<<max(g[1][n][0],g[1][n][1]);
return 0;
}
F题
传送门
题意简述:
有
n
n
n家银行,你每月初可以选择从其中一家贷款
a
i
a_i
ai,然后接下来
k
i
k_i
ki个月(包括这个月)每月底还
b
i
b_i
bi元,问所有贷款方案中在任意一个月中总现额的最大值。
思路:
显然有费用流做法,利用费用提前计算思想即可。
然而也可以贪心的按照
b
i
b_i
bi从大到小排序,然后
f
i
f_i
fi表示总共贷款
i
i
i个月的最大值跑一个
O
(
n
2
)
O(n^2)
O(n2)的
d
p
dp
dp。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const int N=1005,M=250005;
int n;
struct Node{int a,b,c;}a[N];
ll ans=0,f[N];
inline void update(ll&a,const ll&b){a=b>a?b:a;}
inline bool cmp(const Node&a,const Node&b){return a.b>b.b;}
int main(){
n=read();
for(ri i=1;i<=n;++i)a[i].a=read(),a[i].b=read(),a[i].c=read();
sort(a+1,a+n+1,cmp);
for(ri i=1;i<=n;++i){
for(ri j=n-1;~j;--j){
update(f[j+1],f[j]+a[i].a-(ll)j*a[i].b);
update(f[j],f[j]+a[i].a-(ll)a[i].c*a[i].b);
}
}
for(ri i=0;i<=n;++i)update(ans,f[i]);
cout<<ans;
return 0;
G题
传送门
题意简述:
给出
n
,
a
,
c
i
,
d
i
n,a,c_i,d_i
n,a,ci,di,你要从中选出一段
[
l
,
r
]
[l,r]
[l,r]来,贡献是
a
∗
(
r
−
l
+
1
)
−
∑
i
=
l
r
c
i
−
m
a
x
i
=
l
r
−
1
(
d
i
+
1
−
d
i
)
2
a*(r-l+1)-\sum_{i=l}^rc_i-max_{i=l}^{r-1}(d_{i+1}-d_i)^2
a∗(r−l+1)−∑i=lrci−maxi=lr−1(di+1−di)2,保证
d
d
d单增。
思路:
考虑
d
p
dp
dp,
f
i
f_i
fi表示以
i
i
i结尾序列长度至少为
2
2
2的答案。
那么显然可以从
i
i
i开始往前找到一个第一个
j
j
j使得
d
i
−
d
i
−
1
≤
d
j
−
d
j
−
1
d_i-d{i-1}\le d_j-d_{j-1}
di−di−1≤dj−dj−1
这样
f
i
f_i
fi可以由
f
j
f_j
fj和
[
j
,
i
]
[j,i]
[j,i]之间的最大后缀和转移过来,因此我们二分或者线段树求出
j
j
j,然后更新
f
i
f_i
fi即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const int N=3e5+5;
int n;
ll ans=0,f[N],sum[N],a,c[N],d[N];
inline void update(ll&a,const ll&b){a=b>a?b:a;}
namespace SGT{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
ll mx[N<<2];
inline void pushup(int p){mx[p]=max(mx[lc],mx[rc]);}
inline void build(int p,int l,int r){
mx[p]=-1;
if(l==r)return;
build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline int query(int p,int l,int r,ll v){
if(mx[p]<v)return -1;
if(l==r)return l;
return mx[rc]>=v?query(rc,mid+1,r,v):query(lc,l,mid,v);
}
inline void update(int p,int l,int r,int k,ll v){
if(l==r){mx[p]=v;return;}
k<=mid?update(lc,l,mid,k,v):update(rc,mid+1,r,k,v);
pushup(p);
}
#undef mid
#undef lc
#undef rc
}
namespace sgt{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
struct Node{int l,r;ll rs,sum;}T[N<<2];
inline Node operator+(const Node&a,const Node&b){return (Node){a.l,b.r,max(b.rs,b.sum+a.rs),a.sum+b.sum};}
inline void build(int p,int l,int r){
T[p].l=l,T[p].r=r;
if(l==r){T[p].rs=T[p].sum=a-c[l];return;}
build(lc,l,mid),build(rc,mid+1,r),T[p]=T[lc]+T[rc];
}
inline Node query(int p,int ql,int qr){
if(ql<=T[p].l&&T[p].r<=qr)return T[p];
if(qr<=mid)return query(lc,ql,qr);
if(ql>mid)return query(rc,ql,qr);
return query(lc,ql,mid)+query(rc,mid+1,qr);
}
#undef mid
#undef lc
#undef rc
}
inline ll solve(int l,int r){return sgt::query(1,l,r-1).rs+a-c[r]-(d[r]-d[r-1])*(d[r]-d[r-1]);}
int main(){
n=read(),a=read(),SGT::build(1,1,n);
for(ri i=1;i<=n;++i)d[i]=read(),c[i]=read(),update(ans,a-c[i]),sum[i]=sum[i-1]+a-c[i];
sgt::build(1,1,n);
for(ri i=1;i<=n;++i){
f[i]=-1e18;
if(i==1)continue;
int pos=SGT::query(1,1,n,d[i]-d[i-1]);
if(pos==-1)pos=1;
f[i]=max(solve(pos,i),f[pos]+sum[i]-sum[pos]);
update(ans,f[i]),SGT::update(1,1,n,i,d[i]-d[i-1]);
}
cout<<ans;
return 0;
}