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 read()
{
	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 read()
{
	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()
{
	n=read();
	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 read()
{
	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()
{
	n=read(),m=read();
	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 read()
{
	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));
	m=read(),n=read(),q=read();
	for (int i=1;i<=m;i++) a[read()]++;
	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;
		int x=read()-m;
		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 read()
{
	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()
{
	n=read();
	for (int i=1;i<=n;i++)
	{
		b[++t]=a[i].x1=read();
		b[++t]=a[i].y1=read();
		b[++t]=a[i].x2=read();
		b[++t]=a[i].y2=read();
	}
	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 read()
{
	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
	n=read();
	for (int i=1;i<=n;i++) a[i].x1=read(),a[i].y1=read(),a[i].x2=read(),a[i].y2=read();
	if (solve(a,n)) cout<<"YES";else cout<<"NO";
	return 0;
	//NOTICE LONG LONG!!!!!
}

  小小小小号。result:rank 3 rating +218

 

posted @ 2019-06-20 21:33  Gloid  阅读(261)  评论(0编辑  收藏  举报