要是有题目FST了就重新写
A
签到
#include<bits/stdc++.h> using namespace std; int T; long long n,k,ans; int main() { cin>>T; while(T--) { cin>>n>>k,ans=0; while(n) { ans+=n%k; ans++,n/=k; } cout<<ans-1<<endl; } }
B
暴力模拟,记得打标记和开long long,注意特判
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+7; int T,top; char str[101]; ll ans,st[N]; int main() { scanf("%d",&T); st[0]=1; while(T--) { scanf("%s",str); if(str[0]=='a') { if(st[top]==-1)ans=-1; else ans+=st[top]; if(st[top]==-1||ans>=(1ll<<32)){puts("OVERFLOW!!!");return 0;} } else if(str[0]=='f') { ll x;scanf("%I64d",&x); if(st[top]==-1)st[top+1]=-1; else st[top+1]=x*st[top]; top++; if(st[top]>=(1ll<<32))st[top]=-1; } else top--; } cout<<ans; }
C
我写个O(nlogn)复杂了,实际上是签到题,我还写个二分
#include<bits/stdc++.h> using namespace std; const int N=2e5+7; int n,k,pos,a[N]; bool check(int d) { for(int l=1,r=1;l<=n;l++) { while(r<n&&a[r+1]-a[l]<=2*d)r++; if(r-l+1>k){pos=a[l]+d;return 1;} } return 0; } int main() { int T;scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]); int L=0,R=a[n]-a[1],mid,ans=R; while(L<=R) { mid=L+R>>1; if(check(mid))ans=mid,R=mid-1; else L=mid+1; } printf("%d ",pos); } }
D
发现是k段后缀和,其中[1,n]必须得选,其余[2,n]...[n,n]选最大的k段即可。
#include<bits/stdc++.h> using namespace std; const int N=3e5+7; int n,k,a[N]; long long ans,s[N]; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]); if(n==1){printf("%d",a[1]);return 0;} for(int i=n;i;i--)s[i]=s[i+1]+a[i]; sort(s+2,s+n+1); reverse(s+2,s+n+1); for(int i=1;i<=k;i++)ans+=s[i]; cout<<ans; }
E
首先求出每个位置仅用一条线段能走到哪,这个显然可以线段树覆盖(实际上排序也许,不过线段树太好想了)。然后愉快地倍增即可。
#include<bits/stdc++.h> #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; const int N=5e5+7; int n,m,all=5e5,lazy[N<<2],mx[N<<2],fa[N][20]; void modify(int rt,int v){lazy[rt]=max(lazy[rt],v),mx[rt]=max(mx[rt],v);} void pushdown(int rt) { if(!lazy[rt])return; modify(rt<<1,lazy[rt]),modify(rt<<1|1,lazy[rt]); lazy[rt]=0; } void update(int L,int R,int v,int l,int r,int rt) { if(L<=l&&r<=R){modify(rt,v);return;} pushdown(rt); int mid=l+r>>1; if(L<=mid)update(L,R,v,lson); if(R>mid)update(L,R,v,rson); mx[rt]=max(mx[rt],mx[rt<<1|1]); } void query(int l,int r,int rt) { if(l==r){fa[l][0]=max(l,mx[rt]);return;} pushdown(rt); int mid=l+r>>1; query(lson),query(rson); } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<=n;i++)scanf("%d%d",&x,&y),update(x,y,y,0,all,1); query(0,all,1); for(int j=1;j<=19;j++) for(int i=0;i<=all;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; while(m--) { int x,y,ans=0;scanf("%d%d",&x,&y); for(int i=19;~i;i--)if(fa[x][i]<y)x=fa[x][i],ans+=(1<<i); if(fa[x][0]<y)puts("-1"); else printf("%d ",ans+1); } }
F
我们可以从后向前扫,然后找以i为起点的答案。显然区间内不能有相同的数字,于是可以记录nxt[i]数组表示从i位置向后延展到nxt[i]-1处数字均互不相同,这个显然线性维护。由于区间要求值为[1,len],所以很容易发现越短的区间最大值越小,所以从后向前扫时,仅需维护一个单调递减的队列,然后对于队列中相邻的数,根据位置和该位置的值,建立树状数组即可。复杂度O(nlogn)
#include<bits/stdc++.h> using namespace std; const int N=3e5+7; int n,qs,qe,a[N],b[N],c[N],nxt[N],q[N]; long long ans; void add(int x,int v){if(x>0)while(x<=n)c[x]+=v,x+=x&-x;} int ask(int x){int ret=0;while(x)ret+=c[x],x-=x&-x;return ret;} int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); nxt[n+1]=n+1; for(int i=1;i<=n;i++)b[i]=n+1; for(int i=n;i;i--) { nxt[i]=min(nxt[i+1],b[a[i]]),b[a[i]]=i; while(qs<qe&&a[q[qe-1]]<=a[i]) { if(qs+1<qe)add(q[qe-2]-a[q[qe-1]],-1); qe--; } q[qe++]=i; if(qs+1<qe)add(q[qe-2]-a[q[qe-1]],1); while(qs<qe&&q[qs]>=nxt[i]) { if(qs+1<qe)add(q[qs]-a[q[qs+1]],-1); qs++; } ans+=ask(n)-ask(i-1); if(nxt[i]-a[q[qs]]>=i)ans++; } cout<<ans; }
G
首先显然分层DP,朴素的O(n2k)DP相信考过提高组的人都会,然后考虑如何优化。看着不大的数据范围,想到可能是O(nklogn)这种奇怪的复杂度,然后看着就想到直接分治。分治后,记录从分治点向两边的前缀/后缀最大值,可以跑一遍two-pointer,然后发现每个位置的答案是一次函数,然后就可以写个单调栈就行了。
#include<bits/stdc++.h> using namespace std; const int N=20086,inf=0x3f3f3f3f; struct line{ int k,b; line(){} line(int _k,int _b){k=_k,b=_b;} int get(int x){return k*x+b;} }st[N]; int n,k,top,a[N],f[N],g[N],pre[N],suf[N]; bool cmp(line a,line b,line c){return 1ll*(a.k-b.k)*(c.b-a.b)<=1ll*(a.k-c.k)*(b.b-a.b);} void insert(line x) { while(top&&x.k>=st[top].k)x.b=min(x.b,st[top].b),top--; while(top>1&&cmp(st[top-1],st[top],x))top--; st[++top]=x; } int calc(int x) { if(!top)return inf; int l=1,r=top; while(l<r) { int m=l+r>>1; if(st[m].get(x)<=st[m+1].get(x))r=m;else l=m+1; } return st[l].get(x); } void solve(int l,int r) { if(l==r)return; int m=l+r>>1; solve(l,m),solve(m+1,r); suf[m+1]=0;for(int i=m;i>=l;i--)suf[i]=max(suf[i+1],a[i]); pre[m]=0;for(int i=m+1;i<=r;i++)pre[i]=max(pre[i-1],a[i]); top=0; for(int i=r,j=l;i>m;i--) { while(j<=m&&suf[j+1]>=pre[i])if(g[j++]<inf)insert(line(suf[j],g[j-1]-(j-1)*suf[j])); f[i]=min(f[i],calc(i)); } top=0; for(int i=m+1,j=m;i<=r;i++) { while(j>=l&&suf[j+1]<=pre[i])if(g[j--]<inf)insert(line(j+1,g[j+1])); f[i]=min(f[i],calc(-pre[i])+i*pre[i]); } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]),pre[i]=max(pre[i-1],a[i]); for(int i=1;i<=n;i++)f[i]=pre[i]*i; for(int i=2;i<=k;i++)memcpy(g,f,sizeof g),memset(f,0x3f,sizeof f),solve(1,n); printf("%d",f[n]); }
result:rank11,rating+=159,精准的没超过rating=2210的小号。