# Codeforces Round #567 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;
}
ll x,y,z;
signed main()
{
cin>>x>>y>>z;
cout<<(x+y)/z<<' ';
if (x%z+y%z<z) cout<<0;
else cout<<min(z-x%z,z-y%z);
return 0;
//NOTICE LONG LONG!!!!!
}


B：显然在中间截比较优。于是就找到在左侧和在右侧的最靠近中点的切割点。注意不能有前导0。高精加即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 100010
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],b[N],c[N],d[N],len1=N,len2=N;
char s[N];
signed main()
{
scanf("%s",s+1);
int x=0,y=n+1;
for (int i=1;i<n;i++)
if (s[i+1]!='0')
{
if (i<=n/2) x=max(x,i);
else y=min(y,i);
}
if (x>0)
{
int lena=0,lenb=0;
for (int i=x;i>=1;i--) a[++lena]=s[i]-'0';
for (int i=n;i>x;i--) b[++lenb]=s[i]-'0';
for (int i=1;i<=max(lena,lenb);i++)
{
a[i]+=b[i];
a[i+1]+=a[i]/10;
a[i]%=10;
}
len1=max(lena,lenb);
if (a[len1+1]) len1++;
}
if (y<=n)
{
int lena=0,lenb=0;
for (int i=y;i>=1;i--) c[++lena]=s[i]-'0';
for (int i=n;i>y;i--) d[++lenb]=s[i]-'0';
for (int i=1;i<=max(lena,lenb);i++)
{
c[i]+=d[i];
c[i+1]+=c[i]/10;
c[i]%=10;
}
len2=max(lena,lenb);
if (c[len2+1]) len2++;
}
if (len1<len2)
{
for (int i=len1;i>=1;i--) printf("%d",a[i]);
}
else
if (len1>len2)
{
for (int i=len2;i>=1;i--) printf("%d",c[i]);
}
else
{
bool flag1=0;
for (int i=len1;i>=1;i--)
if (a[i]!=c[i])
{
flag1=a[i]<c[i];
break;
}
if (flag1) for (int i=len1;i>=1;i--) printf("%d",a[i]);
else for (int i=len2;i>=1;i--) printf("%d",c[i]);
}
return 0;
//NOTICE LONG LONG!!!!!
}


C：每个点处理出该列中以该位置为起点形成的三段是什么样子的。注意第三段长度对第二段取min。然后枚举每一行，找到三段相同且合法的子段计算贡献即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 1010
#define mp(x,y) make_pair((x),(y))
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;
}
typedef pair<char,int> pii;
int n,m;
pii f[N][N][3];
char a[N][N];
ll ans;
bool issame(int i,int x,int y)
{
for (int j=0;j<3;j++)
if (f[i][x][j]!=f[i][y][j]) return 0;
return 1;
}
signed main()
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]=getc();
for (int i=1;i<=m;i++) f[n][i][0]=mp(a[n][i],1);
for (int i=n-1;i>=1;i--)
for (int j=1;j<=m;j++)
if (a[i][j]==a[i+1][j])
{
for (int k=0;k<3;k++) f[i][j][k]=f[i+1][j][k];
f[i][j][0].second++;
}
else
{
for (int k=1;k<3;k++) f[i][j][k]=f[i+1][j][k-1];
f[i][j][0]=mp(a[i][j],1);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
f[i][j][2].second=min(f[i][j][2].second,f[i][j][1].second);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
int t=j;
while (t<m&&issame(i,j,t+1)) t++;
if (f[i][j][0].second==f[i][j][1].second&&f[i][j][1].second==f[i][j][2].second) ans+=(t-j+1)*(t-j+2)/2;
j=t;
}
}
cout<<ans;
return 0;
//NOTICE LONG LONG!!!!!
}


D：可以首先对每个询问二分出该次要加的数的值是多少。然后问题变为多次询问序列中第k个<=p的数是多少。可以将数从小到大考虑，treap维护查询第k大。事实上直接离线即可，开始的二分有点多余。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 500010
#define int long long
#define lson tree[k].ch[0]
#define rson tree[k].ch[1]
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,q,a[N],b[N],s[N],cnt[N],root,Cnt;
struct data
{
int k,p,i,ans;
bool operator <(const data&a) const
{
return i<a.i;
}
}Q[N];
bool cmp(const data&a,const data&b)
{
return a.p<b.p;
}
vector<int> pos[N];
struct data2{int ch[2],s,p,x;
}tree[N];
void up(int k){tree[k].s=tree[lson].s+tree[rson].s+1;}
void move(int &k,int p)
{
int t=tree[k].ch[p];
tree[k].ch[p]=tree[t].ch[!p],tree[t].ch[!p]=k,up(k),up(t),k=t;
}
void ins(int &k,int x)
{
if (k==0) {k=++Cnt;tree[k].x=x,tree[k].p=rand(),tree[k].s=1;return;}
tree[k].s++;
if (tree[k].x<x) {ins(rson,x);if (tree[rson].p>tree[k].p) move(k,1);}
else {ins(lson,x);if (tree[lson].p>tree[k].p) move(k,0);}
}
int query(int k,int x)
{
if (tree[lson].s+1==x) return tree[k].x;
if (tree[lson].s+1>x) return query(lson,x);
else return query(rson,x-tree[lson].s-1);
}
signed main()
{
srand(time(0));
for (int i=1;i<=n;i++) pos[a[i]].push_back(i);
for (int i=1;i<=n;i++) b[a[i]]++;cnt[0]=b[0];
for (int i=1;i<=m;i++) s[i]=s[i-1]+i*b[i],cnt[i]=cnt[i-1]+b[i];
for (int i=1;i<=q;i++)
{
Q[i].i=i;
if (x+m>m*n) Q[i].k=(x+m-1)%n+1,Q[i].p=m;
else
{
int l=0,r=m,p=0;
while (l<=r)
{
int mid=l+r>>1;
if (cnt[mid]*mid-s[mid]<x) p=mid,l=mid+1;
else r=mid-1;
}
int k=x-(cnt[p]*p-s[p]);
Q[i].k=k,Q[i].p=p;
}
}
sort(Q+1,Q+q+1,cmp);
int cur=0;
for (int i=0;i<=m;i++)
{
for (int j:pos[i]) ins(root,j);
while (cur<q&&Q[cur+1].p==i)
{
cur++;
Q[cur].ans=query(root,Q[cur].k);
}
}
sort(Q+1,Q+q+1);
for (int i=1;i<=q;i++) printf("%d\n",Q[i].ans);
return 0;
//NOTICE LONG LONG!!!!!
}


E：E1就是个思博暴力，显然能切就切不会改变可行性。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 100010
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,b[N<<2],t;
int cnt[N<<2];
struct data{int x1,y1,x2,y2;
}a[N],c[N];
bool solve(int l,int r)
{
if (l==r) return 1;
for (int i=1;i<=t;i++) cnt[i]=0;
for (int i=l;i<=r;i++) cnt[a[i].x1+1]++,cnt[a[i].x2]--;
int mx=0,mn=t+1;
for (int i=l;i<=r;i++) mx=max(mx,a[i].x1),mn=min(mn,a[i].x2);
int s=0,p=-1;
for (int i=1;i<=t;i++)
{
s+=cnt[i];
if (i>=mn&&i<=mx&&s==0) {p=i;break;}
}
if (p!=-1)
{
int u=l-1;
for (int i=l;i<=r;i++)
if (a[i].x2<=p) c[++u]=a[i];
int mid=u;
for (int i=l;i<=r;i++)
if (a[i].x1>=p) c[++u]=a[i];
for (int i=l;i<=r;i++) a[i]=c[i];
return solve(l,mid)&&solve(mid+1,r);
}
for (int i=1;i<=t;i++) cnt[i]=0;
for (int i=l;i<=r;i++) cnt[a[i].y1+1]++,cnt[a[i].y2]--;
mx=0,mn=t+1;
for (int i=l;i<=r;i++) mx=max(mx,a[i].y1),mn=min(mn,a[i].y2);
s=0,p=-1;
for (int i=1;i<=t;i++)
{
s+=cnt[i];
if (i>=mn&&i<=mx&&s==0) {p=i;break;}
}
if (p!=-1)
{
int u=l-1;
for (int i=l;i<=r;i++)
if (a[i].y2<=p) c[++u]=a[i];
int mid=u;
for (int i=l;i<=r;i++)
if (a[i].y1>=p) c[++u]=a[i];
for (int i=l;i<=r;i++) a[i]=c[i];
return solve(l,mid)&&solve(mid+1,r);
}
return 0;
}
signed main()
{
for (int i=1;i<=n;i++)
{
}
sort(b+1,b+t+1);
t=unique(b+1,b+t+1)-b-1;
for (int i=1;i<=n;i++)
{
a[i].x1=lower_bound(b+1,b+t+1,a[i].x1)-b;
a[i].y1=lower_bound(b+1,b+t+1,a[i].y1)-b;
a[i].x2=lower_bound(b+1,b+t+1,a[i].x2)-b;
a[i].y2=lower_bound(b+1,b+t+1,a[i].y2)-b;
}
if (solve(1,n)) cout<<"YES";else cout<<"NO";
return 0;
//NOTICE LONG LONG!!!!!
}


E2的话，基本思路肯定没法有什么变化，如果要保证复杂度，容易想到每次切割都只能花费相当于较少部分矩形个数的代价。那么首先切割时不能把两部分分别递归下去，而是要递归较少的部分并把其从整体中删掉，可以链表实现。然后考虑怎样以这样的复杂度找到一种切割方案。将矩形集合复制成四份，并分别按x1,x2,y1,y2排序，同时处理四份矩形集合，找到一种切割时就立即停止，这样就能保证扫过的部分矩形数量不大于一半了。判断能否切割只需要记录另一维坐标的极值，比如对于x1从小到大排序后，记录x2的最大值即可。实现时像我一样蠢的话就会出现大量的同一份代码写四次。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 100010
#define del(x) {nxt1[pre1[pos1[x]]]=nxt1[pos1[x]];pre1[nxt1[pos1[x]]]=pre1[pos1[x]];nxt2[pre2[pos2[x]]]=nxt2[pos2[x]];pre2[nxt2[pos2[x]]]=pre2[pos2[x]];nxt3[pre3[pos3[x]]]=nxt3[pos3[x]];pre3[nxt3[pos3[x]]]=pre3[pos3[x]];nxt4[pre4[pos4[x]]]=nxt4[pos4[x]];pre4[nxt4[pos4[x]]]=pre4[pos4[x]];}
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;
struct data{int x1,y1,x2,y2,i;
}a[N];
bool cmp1(const data&a,const data&b)
{
return a.x1<b.x1;
}
bool cmp2(const data&a,const data&b)
{
return a.x2>b.x2;
}
bool cmp3(const data&a,const data&b)
{
return a.y1<b.y1;
}
bool cmp4(const data&a,const data&b)
{
return a.y2>b.y2;
}
bool solve(data *a,int n)
{
data a1[n+1],a2[n+1],a3[n+1],a4[n+1];
int nxt1[n+2],nxt2[n+2],nxt3[n+2],nxt4[n+2];
int pre1[n+2],pre2[n+2],pre3[n+2],pre4[n+2];
for (int i=0;i<=n;i++)
{
a[i].i=i;
a1[i]=a2[i]=a3[i]=a4[i]=a[i];
nxt1[i]=nxt2[i]=nxt3[i]=nxt4[i]=i+1;
pre1[i+1]=pre2[i+1]=pre3[i+1]=pre4[i+1]=i;
}
sort(a1+1,a1+n+1,cmp1);sort(a2+1,a2+n+1,cmp2);sort(a3+1,a3+n+1,cmp3);sort(a4+1,a4+n+1,cmp4);
int pos1[n+2],pos2[n+2],pos3[n+2],pos4[n+2];
for (int i=1;i<=n;i++) pos1[a1[i].i]=i,pos2[a2[i].i]=i,pos3[a3[i].i]=i,pos4[a4[i].i]=i;
while (n>1)
{
int tmp=0;
int mxx2=-inf,mnx1=inf,mxy2=-inf,mny1=inf;
int cur1=0,cur2=0,cur3=0,cur4=0;
bool flag=0;
for (int i=1;i<=n/2;i++)
{
cur1=nxt1[cur1];
cur2=nxt2[cur2];
cur3=nxt3[cur3];
cur4=nxt4[cur4];
mxx2=max(mxx2,a1[cur1].x2);
mnx1=min(mnx1,a2[cur2].x1);
mxy2=max(mxy2,a3[cur3].y2);
mny1=min(mny1,a4[cur4].y1);
if (mxx2<=a1[nxt1[cur1]].x1)
{
flag=1;
data b[i+1];
for (int j=1;j<=i;j++)
{
b[j]=a1[cur1];
int x=a1[cur1].i;
cur1=pre1[cur1];
del(x);
}
if (!solve(b,i)) return 0;
else {n-=i;break;}
}
if (mnx1>=a2[nxt2[cur2]].x2)
{
flag=1;
data b[i+1];
for (int j=1;j<=i;j++)
{
b[j]=a2[cur2];
int x=a2[cur2].i;
cur2=pre2[cur2];
del(x);
}
if (!solve(b,i)) return 0;
else {n-=i;break;}
}
if (mxy2<=a3[nxt3[cur3]].y1)
{
flag=1;
data b[i+1];
for (int j=1;j<=i;j++)
{
b[j]=a3[cur3];
int x=a3[cur3].i;
cur3=pre3[cur3];
del(x);
}
if (!solve(b,i)) return 0;
else {n-=i;break;}
}
if (mny1>=a4[nxt4[cur4]].y2)
{
flag=1;
data b[i+1];
for (int j=1;j<=i;j++)
{
b[j]=a4[cur4];
int x=a4[cur4].i;
cur4=pre4[cur4];
del(x);
}
if (!solve(b,i)) return 0;
else {n-=i;break;}
}
}
if (!flag) return 0;
}
return 1;
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif