比赛链接
这场前面顺,中间卡,后面用线段树模板过了一道题,剩下两题,倒二题是个背包DP,思想转换很重要
A题.
特判一下i和i-1就可
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
string s;
cin>>s;
for(int i=0;i<n+1/2;i++){
if(s[i]==s[n-i-1]){
cout<<n-2*i<<endl;
goto l;
}
}
cout<<'0'<<endl;
l:;
}
}
B题:
使用前缀和求f左,然后后缀和求f右,max一下最大的f左+f右;
#include<bits/stdc++.h>
using namespace std;
int l[200005],r[200005];
set<char> ql;
set<char> qr;
void solve(){
ql.clear();
qr.clear();
int n;
cin>>n;
for(int i=0;i<n;i++){
l[i]=r[i]=0;
}
string s;
cin>>s;
for(int i=0;i<n;i++){
ql.insert(s[i]);
l[i]=ql.size();
}
reverse(s.begin(),s.end());
for(int i=0;i<n;i++){
qr.insert(s[i]);
r[i]=qr.size();
}
int ans=0;
for(int i=0;i<n-1;i++){
ans=max(ans,l[i]+r[n-1-i-1]);
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
}
C题:
规律题,操作等价于将两个数符号相换,因此如果有0或者负号为偶数,则直接输出绝对值之和,else输出减去2倍的绝对值最小的那个数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[200005];
void solve(){
ll n;
cin>>n;
bool g=0;
int f=0;
ll s=0;
for(int i=0;i<n;i++){
cin>>a[i];
if(a[i]==0){
g=1;
}
if(a[i]<0)
f++;
a[i]=abs(a[i]);
s+=abs(a[i]);
}
if(g||f%2==0){
cout<<s<<endl;
return ;
}
ll ns=1e10;
for(int i=0;i<n;i++){
if(a[i]<ns){
ns=a[i];
}
}
s-=2*ns;
cout<<s<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
}
D题
可知如果一个数进行有限次的操作就会变成个位数,因此用线段树/树状数组维护一下每个位置目前操作了多少次,然后查询次数后,把那个数操作一下,记得如果是个位数了,那么break
#include<iostream>
#include<cstdio>
#define MAXN 200005
#define ll long long
using namespace std;
const int maxn=200010;
int vis[maxn];
int a[maxn+2];
struct tree{
int l,r;
long long pre,add;
}t[4*maxn+2];
void bulid(int p,int l,int r){
t[p].l=l;t[p].r=r;
if(l==r){
t[p].pre=a[l];
return;
}
int mid=l+r>>1;
bulid(p*2,l,mid);
bulid(p*2+1,mid+1,r);
t[p].pre=t[p*2].pre+t[p*2+1].pre;
}
void spread(int p){
if(t[p].add){
t[p*2].pre+=t[p].add*(t[p*2].r-t[p*2].l+1);
t[p*2+1].pre+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
t[p*2].add+=t[p].add;
t[p*2+1].add+=t[p].add;
t[p].add=0;
}
}
void change(int p,int x,int y,int z){
if(x<=t[p].l && y>=t[p].r){
t[p].pre+=(long long)z*(t[p].r-t[p].l+1);
t[p].add+=z;
return;
}
spread(p);
int mid=t[p].l+t[p].r>>1;
if(x<=mid) change(p*2,x,y,z);
if(y>mid) change(p*2+1,x,y,z);
t[p].pre=t[p*2].pre+t[p*2+1].pre;
}
long long ask(int p,int x,int y){
if(x<=t[p].l && y>=t[p].r) return t[p].pre;
spread(p);
int mid=t[p].l+t[p].r>>1;
long long ans=0;
if(x<=mid) ans+=ask(p*2,x,y);
if(y>mid) ans+=ask(p*2+1,x,y);
return ans;
}
ll ans(ll x,ll g){
ll ss=x;
while(g--){
ss=0;
while(x!=0){
ss+=x%10;
x/=10;
}
x=ss;
if(ss/10==0)
break;
}
return ss;
}
void solve(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<=n+2;i++){
a[i]=0;
}
for(int i=0;i<=4*n+2;i++){
t[i].l=t[i].r=t[i].pre=t[i].add=0;
}
for(int i=1;i<=n;i++)
scanf("%d",&vis[i]);
bulid(1,1,n);
for(int i=1;i<=m;i++)
{
int q,x,y,z;
scanf("%d",&q);
if(q==1){
scanf("%d%d",&x,&y);
change(1,x,y,1);
}
else {
scanf("%d",&x);
int g=ask(1,x,x);
printf("%lld\n",ans(vis[x],g));
}
}
}
int main()
{
int t;
cin>>t;
while(t--){
solve();
}
}
E题
这题读懂题很简单,取两个min
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct ad{
int a,b,c,d;
}q[5];
int main(){
int n;
cin>>n;
for(int i=1;i<=4;i++){
cin>>q[i].a>>q[i].b>>q[i].c>>q[i].d;
}
for(int i=1;i<=4;i++){
int s1=min(q[i].a,q[i].b);
int s2=min(q[i].c,q[i].d);
if(s1+s2<=n){
cout<<i<<' '<<s1<<' '<<n-s1<<endl;
return 0;
}
}
cout<<"-1";
}
F题
这题被卡了好几次,当是0时特判一下
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int a[100005],s[100005];
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
s[i%k]+=a[i];
}
s[k]=s[0];
int mi,wz=1;
mi=1e10;
for(int i=1;i<=k;i++){
if(s[i]<mi){
wz=i;
mi=s[i];
}
}
cout<<wz;
}
G题:
- ∑ai/∑bi=k
- ∑ai−k×∑bi=0
- ∑(ai−kbi)=0
- 非常巧妙的DP,构造一个新的背包c,装可以组成的数,如果ci是负的,那么dp1[j]=max(dp1[j],dp1[j-c[i]+a[i]),如果是正的,dp[j]=max(dp[j+c[i]+a[i]);
#include<bits/stdc++.h>
using namespace std;
int a[105],b[105],c[105];
int dp1[101005],dp2[110005];
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++){
c[i]=a[i]-k*b[i];
}
int m=10002;
for(int i=1;i<=m;i++){
dp1[i]=dp2[i]=-1e9;
}//i时从1开始的
for(int i=1;i<=n;i++){
if(c[i]>=0){
for(int j=1e4;j>=c[i];j--){
dp1[j]=max(dp1[j],dp1[j-c[i]]+a[i]);
}
}
else{
c[i]=-c[i];
for(int j=1e4;j>=c[i];j--){
dp2[j]=max(dp2[j],dp2[j-c[i]]+a[i]);
}
}
}
int ans=0;
for(int i=0;i<=1e4;i++){
ans=max(ans,dp1[i]+dp2[i]);
}
if(ans){
cout<<ans<<endl;
return 0;
}
cout<<"-1";
}
H题:
(u,l,r)表示到达u时所能携带数的区间。然后做一个搜索加剪枝即可。 搜索到达每一点的区间,ans=max(ans,r-l+1)
-
剪枝如下:
-
最优性剪枝,如果当前区间小于已知解,停止搜索。
-
记忆化,用set维护每个点被访问时的区间,如果重复出现,停止搜索。
#include<bits/stdc++.h> using namespace std; int n,m; int ans; int main(){ cin>>n>>m; vector<set<pair<int, int>>> vis(n + 1); vector<vector<tuple<int, int, int>>> e(n + 1); for(int i=0;i<m;i++){ int a,b,l,r; cin>>a>>b>>l>>r; e[a].emplace_back(b,l,r); e[b].emplace_back(a,l,r); } vis[1].emplace(1, 1e6); queue<tuple<int,int,int>>q; q.emplace(1 ,1 ,1e6); while(!q.empty()){ auto [a,l,r]=q.front(); q.pop(); if(a==n){ ans=max(r-l+1,ans); continue; } int ll,rr; for(auto [b,lg,rg]:e[a]){ ll=max(l,lg),rr=min(rg,r); if(ll>rr||rr-ll+1<=ans){ continue; } if (vis[b].emplace(ll, rr).second == false) continue; q.emplace(b,ll,rr); } } if(ans==0){ cout<<"Nice work, Dima!"; } else{ cout<<ans; } }