Educational Codeforces Round 66 (Rated for Div. 2)

A：显然能除就除。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int t;
ll a,b;
signed main()
{
while (t--)
{
cin>>a>>b;
ll ans=0;
while (a)
{
ans+=a%b;a-=a%b;
if (a==0) break;a/=b;ans++;
}
cout<<ans<<endl;
}
return 0;
//NOTICE LONG LONG!!!!!
}


B：栈维护。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000000010ll
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int T,stk[100010],top;ll mul[100010],ans;
signed main()
{
while (T--)
{
char c=getc();getc(),getc();
if (c=='a')
{
ans+=mul[top];if (ans>=4294967296ll) break;
}
if (c=='f')
{
}
if (c=='e')
{
top--;
}
}
if (ans>=4294967296ll) cout<<"OVERFLOW!!!";
else cout<<ans;
return 0;
//NOTICE LONG LONG!!!!!
}


C：枚举每个长度为k的子段取两端点距离/2（向上取整）更新答案即可。因为显然对于一个固定点，要找k个点使得离它的最远点最近，一定是k个点连成一个子段最优。于是可以反过来对每个子段求最优点。

 #include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int T,n,k,a[N];
signed main()
{
while (T--)
{
int u=inf,pos=0;
for (int i=1;i<=n;i++)
if (i+k<=n)
{
u=min(u,a[i+k]-a[i]+1>>1);
if ((a[i+k]-a[i]+1>>1)==u) pos=a[i+k]+a[i]>>1;
}
printf("%d\n",pos);
}
return 0;
//NOTICE LONG LONG!!!!!
}


D：将后缀和排序，取整个序列及除此之外最优的k-1个后缀。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 300010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m;
ll a[N],ans;
signed main()
{
for (int i=n;i>=1;i--) a[i]+=a[i+1];
sort(a+2,a+n+1);ans+=a[1];m--;
for (int i=n;i>=n-m+1;i--) ans+=a[i];
cout<<ans;
return 0;
//NOTICE LONG LONG!!!!!
}

E：对每个点求出包含它的区间的右端点最右是哪个。然后倍增求出跳2k个区间后能到哪。查询时从l往r跳即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 200010
#define M 500010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m,nxt[M][21],mx[M<<2];
struct data{int l,r;
}a[N];
void cover(int k,int l,int r,int x,int y,int p)
{
if (l==x&&r==y) {mx[k]=max(mx[k],p);return;}
int mid=l+r>>1;
if (y<=mid) cover(k<<1,l,mid,x,y,p);
else if (x>mid) cover(k<<1|1,mid+1,r,x,y,p);
else cover(k<<1,l,mid,x,mid,p),cover(k<<1|1,mid+1,r,mid+1,y,p);
}
void down(int k,int l,int r)
{
mx[k]=max(mx[k],mx[k>>1]);
if (l==r) {nxt[l][0]=mx[k];return;}
int mid=l+r>>1;
down(k<<1,l,mid);down(k<<1|1,mid+1,r);
}
signed main()
{
for (int i=1;i<=n;i++)
{
cover(1,0,500000,a[i].l,a[i].r,a[i].r);
}
down(1,0,500000);
for (int j=1;j<=20;j++)
for (int i=0;i<=500000;i++)
if (nxt[i][j-1]!=-1) nxt[i][j]=nxt[nxt[i][j-1]][j-1];
for (int i=1;i<=m;i++)
{
if (nxt[l][20]<r) printf("-1\n");
else
{
int ans=0;
for (int j=20;~j;j--)
if (nxt[l][j]<r) ans+=1<<j,l=nxt[l][j];
printf("%d\n",ans+1);
}
}
return 0;
//NOTICE LONG LONG!!!!!
}


F：根据最大值分治，然后只需要考虑跨过最大值且长度等于最大值的值的区间。容易判断一个区间内是否有相同的数，于是直接暴力就好。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 300010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,a[N],f[N][20],nxt[N],p[N],lg2[N],ans;
int query(int x,int y)
{
return a[f[x][lg2[y-x+1]]]>a[f[y-(1<<lg2[y-x+1])+1][lg2[y-x+1]]]?f[x][lg2[y-x+1]]:f[y-(1<<lg2[y-x+1])+1][lg2[y-x+1]];
}
void solve(int l,int r)
{
if (l>r) return;
int mid=query(l,r);
for (int i=max(mid-a[mid]+1,l);i<=min(mid,r-a[mid]+1);i++)
if (nxt[i]>=i+a[mid]-1) ans++;
solve(l,mid-1),solve(mid+1,r);
}
signed main()
{
for (int i=1;i<=n;i++) f[i][0]=i;
for (int j=1;j<20;j++)
for (int i=1;i<=n;i++)
if (a[f[i][j-1]]>a[f[min(i+(1<<j-1),n)][j-1]]) f[i][j]=f[i][j-1];
else f[i][j]=f[min(i+(1<<j-1),n)][j-1];
for (int i=2;i<=n;i++)
{
lg2[i]=lg2[i-1];
if ((2<<lg2[i])<=i) lg2[i]++;
}
for (int i=1;i<=n;i++) p[i]=n+1;nxt[n+1]=n;
for (int i=n;i>=1;i--)
{
nxt[i]=min(nxt[i+1],p[a[i]]-1);
p[a[i]]=i;
}
solve(1,n);
cout<<ans;
return 0;
//NOTICE LONG LONG!!!!!
}


G：暴力O(n2k)dp显然，即f[i][j]为前i个数划成j段时的最小值，则f[i][j]=min{f[k][j-1]+(i-k)*max{a[k+1..i]}}。考虑转移时使用cdq分治。分最大值在右侧还是左侧考虑。考虑最大值在左侧时，右侧从r向mid+1更新，过程中可以用来转移的左侧区间从l开始不断扩大，即f[i][j]=min{f[k][j-1]+(i-k)*max{a[mid+1..i]}} (max{a[k+1..mid]}<=max{a[mid+1..i]}}。这个东西显然可以写成a*i+b的形式，于是维护一个最小值构成的上凸壳在上面二分即可。最大值在右侧同理，从mid+1向r更新，过程中可以用来转移的左侧区间从mid开始不断扩大。具体实现时注意上面两个过程如果直接做分别是按斜率从大到小加直线和从小到大加直线，要合并起来写的话把后者的斜率取反再将查询的x也取反即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1200000010
#define N 20010
#define M 110
#define double long double
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m,a[N],f[N][M],suf[N],pre[N],tail;
struct line
{
int k,b;
int f(int x){return k*x+b;}
}q[N];
double cross(line x,line y)
{
return (double)(y.b-x.b)/(x.k-y.k);
}
void ins(int k,int b)
{
line t=(line){k,b};
while (tail&&q[tail].k==k) t.b=min(t.b,q[tail--].b);
while (tail>1&&cross(t,q[tail-1])<cross(q[tail],q[tail-1])) tail--;
q[++tail]=t;
}
int query(int x)
{
if (!tail) return inf;
int l=1,r=tail-1,p=tail;
while (l<=r)
{
int mid=l+r>>1;
if (cross(q[mid],q[mid+1])>=x) p=mid,r=mid-1;
else l=mid+1;
}
return q[p].f(x);
}
void solve(int l,int r)
{
if (l==r) return;
int mid=l+r>>1;
solve(l,mid);
for (int k=1;k<=m;k++)
{

suf[mid+1]=0;for (int i=mid;i>=l;i--) suf[i]=max(suf[i+1],a[i]);
pre[mid]=0;for (int i=mid+1;i<=r;i++) pre[i]=max(pre[i-1],a[i]);
int p=l;tail=0;
for (int i=r;i>mid;i--)
{
while (suf[p+1]>=pre[i])
{
if (f[p][k-1]!=f[0][1]) ins(suf[p+1],f[p][k-1]-p*suf[p+1]);
p++;
}
f[i][k]=min(f[i][k],query(i));
}
int mx=0;p=mid;tail=0;if (f[p][k-1]!=f[0][1]) ins(p,f[p][k-1]);
for (int i=mid+1;i<=r;i++)
{
mx=max(mx,a[i]);
while (mx>=a[p]&&p>l)
{
p--;
if (f[p][k-1]!=f[0][1]) ins(p,f[p][k-1]);
}
f[i][k]=min(f[i][k],query(-mx)+i*mx);
}
}
solve(mid+1,r);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif