Mail.Ru Cup 2018 Round 2

  A:阅读理解。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 1010
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,a[N],b[N];
signed main()
{
/*#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#endif*/
	n=read(),m=read();
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=n;i++) b[i]=read();
	if (a[1])
	{
		if (a[m]) {cout<<"YES";return 0;}
		if (b[m])
		{
			for (int i=m+1;i<=n;i++)
			if (a[i]&&b[i]) {cout<<"YES";return 0;}
		}
	}
	cout<<"NO";
	return 0;
	//NOTICE LONG LONG!!!!!
}

  B:即维护一个01序列有多少段1,瞎算算即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#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,m,k,ans;
ll a[N];
signed main()
{
/*#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#endif*/
	n=read(),m=read(),k=read();
	for (int i=1;i<=n;i++) a[i]=read()-k;
	for (int i=1;i<=n;i++)
	if (a[i]>0&&a[i-1]<=0) ans++;
	for (int i=1;i<=m;i++)
	{
		int op=read();
		if (op==0)
		{
			printf("%d\n",ans);
		}
		else
		{
			int x=read(),y=read();
			if (a[x]<=0)
			{
				a[x]+=y;
				if (a[x]>0) ans++,ans-=a[x-1]>0,ans-=a[x+1]>0;
			}
		}
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  C:任取两人各一段连续+区间,容易发现起始位置之差在模gcd(ta,tb)意义下总是相同的。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long

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 l1,r1,t1,l2,r2,t2;
ll cross(ll l1,ll r1,ll l2,ll r2)
{
	if (l1>l2) swap(l1,l2),swap(r1,r2);
	return max(min(r2,r1)-l2+1,0ll);
}
signed main()
{
/*#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#endif*/
	l1=read(),r1=read(),t1=read(),l2=read(),r2=read(),t2=read();
	int x=gcd(t1,t2);
	ll ans=cross(l1,r1,l2,r2);
	int u=(l2-l1)/x;
	ans=max(ans,cross(l1,r1,l2-1ll*u*x,r2-1ll*u*x));
	u--;
	ans=max(ans,cross(l1,r1,l2-1ll*u*x,r2-1ll*u*x));
	u++,u++;
	ans=max(ans,cross(l1,r1,l2-1ll*u*x,r2-1ll*u*x));
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  D:一个比较坑的题。容易想到随便找一个需要改的字符串,对其最短修改串(即去掉前后缀极长不需修改的部分)进行修改,判一下是否对其它串也合法就做完了。但问题是这样并不一定最优,可能需要延长修改部分,达到避免对某些串修改的目的,如ac→bc a→a,不应选a→b而应选ac→bc。于是考虑先把所有不需要改的串去掉,需要改的串求出最短修改串,如果任意一对最短修改串不同即无解。否则由每个最短修改串尽量往两边延伸,得到合法的极长修改串,再对其进行检验即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 3010
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,u,nxt[N],L[N],R[N],len[N];
char a[N][N],b[N][N],anss[N],anst[N];
bool flag[N];
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#endif
	n=read();
	for (int i=1;i<=n;i++) scanf("%s",a[i]+1);
	for (int i=1;i<=n;i++) scanf("%s",b[i]+1);
	for (int i=1;i<=n;i++)
	{
		int m=strlen(a[i]+1);len[i]=m;
		flag[i]=1;
		for (int j=1;j<=m;j++)
		if (a[i][j]!=b[i][j]) {flag[i]=0;break;}
		if (!flag[i])
		{
			int t;
			for (int k=1;k<=m;k++)
			if (a[i][k]!=b[i][k])
			{
				t=m;
				while (a[i][t]==b[i][t]) t--;
				L[i]=k,R[i]=t;
				if (!u) u=t-k+1;else if (t-k+1!=u) {cout<<"NO";return 0;}
				break;
			}
		}
	}
	int l=0,r=0;
	for (int i=1;i<=3001;i++)
	{
		bool f=0;
		for (int j=1;j<=n;j++)
		if (!flag[j]&&L[j]==i) {f=1;break;}
		if (f) {l=i-1;break;}
		char c;
		for (int j=1;j<=n;j++)
		if (!flag[j]) {c=a[j][L[j]-i];break;}
		for (int j=1;j<=n;j++)
		if (!flag[j]) if (c!=a[j][L[j]-i]) {f=1;break;}
		if (f) {l=i-1;break;}
	}
	for (int i=1;i<=3001;i++)
	{
		bool f=0;
		for (int j=1;j<=n;j++)
		if (!flag[j]&&R[j]+i>len[j]) {f=1;break;}
		if (f) {r=i-1;break;}
		char c;
		for (int j=1;j<=n;j++)
		if (!flag[j]) {c=a[j][R[j]+i];break;}
		for (int j=1;j<=n;j++)
		if (!flag[j]) if (c!=a[j][R[j]+i]) {f=1;break;}
		if (f) {r=i-1;break;}
	}
	for (int i=1;i<=n;i++)
	if (!flag[i])
	{
		int v=L[i]-l;u+=l+r;
		for (int j=1;j<=u;j++) anss[j]=a[i][v+j-1],anst[j]=b[i][v+j-1];
		break;
	}
	nxt[0]=-1;
	for (int i=1;i<=u;i++)
	{
		int j=nxt[i-1];
		while (~j&&anss[j+1]!=anss[i]) j=nxt[j];
		nxt[i]=j+1;
	}
	for (int i=1;i<=n;i++)
	{
		int cur=0,m=len[i];
		for (int j=1;j<=m;j++)
		{
			while (~cur&&a[i][j]!=anss[cur+1]) cur=nxt[cur];
			cur++;
			if (cur==u)
			{
				for (int k=j-u+1;k<=j;k++) a[i][k]=anst[k-(j-u+1)+1];
				break;
			}
		}
		for (int j=1;j<=m;j++)
		if (a[i][j]!=b[i][j]) {cout<<"NO";return 0;}
	}
	cout<<"YES"<<endl;
	printf("%s\n",anss+1);printf("%s\n",anst+1);
	return 0;
	//NOTICE LONG LONG!!!!!
}

  E:显然二分答案转换成01序列上的问题,然后问题就变为是否能从s个区间中选m个使其并区间的数字和>=k。显然可以合并掉有包含关系的区间后dp,具体地,设f[i][j]为前i个区间选j个时最多能有多少个1,考虑上个区间与当前区间是否有交,分两种情况转移,没有交直接取最大值,有交的情况用堆(或者单调队列?反正cf跑得快)维护。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define ll long long
#define N 1510
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,s,k,a[N],c[N],f[N][N],ans=-1;
bool flag[N];
struct data
{
	int x,y;
	bool operator <(const data&a) const
	{
		return x<a.x||x==a.x&&y<a.y;
	}
	bool operator ==(const data&a) const
	{
		return x==a.x&&y==a.y;
	}
}b[N],d[N];
priority_queue<int> ins,del;
bool check(int u)
{
	for (int i=1;i<=n;i++)
	if (a[i]<=u) c[i]=1;else c[i]=0;
	for (int i=1;i<=n;i++) c[i]+=c[i-1];
	memset(f,0,sizeof(f));
	for (int j=1;j<=s;j++)
	{
		int x=0,t=0;
		while (!ins.empty()) ins.pop();
		while (!del.empty()) del.pop();
		for (int i=1;i<=m;i++)
		{
			while (x<i&&b[x+1].y<b[i].x) x++,del.push(f[x][j-1]-c[b[x].y]),t=max(t,f[x][j-1]);
			while (!del.empty()&&ins.top()==del.top()) ins.pop(),del.pop();
			f[i][j]=max(t+c[b[i].y]-c[b[i].x-1],c[b[i].y]+(ins.empty()?-1000000:ins.top()));
			ins.push(f[i][j-1]-c[b[i].y]);
		}
	}
	for (int i=1;i<=m;i++) if (f[i][s]>=k) return 1;
	return 0;
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#endif
	n=read(),m=read(),s=read(),k=read();
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=m;i++) b[i].x=read(),b[i].y=read();
	sort(b+1,b+m+1);
	m=unique(b+1,b+m+1)-b-1;
	for (int i=1;i<=m;i++)
		for (int j=1;j<=m;j++)
		if (i!=j&&b[j].x<=b[i].x&&b[j].y>=b[i].y) {flag[i]=1;break;}
	int t=0;
	for (int i=1;i<=m;i++)
	if (!flag[i]) d[++t]=b[i];
	m=t;s=min(s,m);
	for (int i=1;i<=m;i++) b[i]=d[i];
	sort(b+1,b+m+1);
	int l=1,r=1000000000;
	while (l<=r)
	{
		int mid=l+r>>1;
		if (check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  F:冷静一下可以发现,由异或的消去性,这个题和树没有任何关系,求出每个点到根的路径的异或和后,就变为一堆数的问题。容易想到的做法是建一棵trie,二分答案,对每个数trie上跑一遍。时间复杂度O(nlog2v),空间复杂度O(nlogv),都炸掉了。

  先考虑优化时间复杂度。trie还是要建的,改为按位考虑。对于当前位是否填1,记录所有需要考虑的对(即异或后与已确定位完全相同),算出下一位均填0和均填1的方案数(即异或的下一位填0),与k进行比较确定答案,如果填1就将k减掉该方案数。然后给每个对拓展下一位。注意到考虑第i位时,所有对都处于trie的第i层,并且每个点只会被考虑两次,所以时间复杂度O(nlogv)。

  然后考虑将空间复杂度优化至线性。将所有数排序,就得到了所谓的隐式trie,因为排序后的一段区间就对应了trie上的一棵子树。跑同样的做法即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define ll long long
#define N 1000010
ll read()
{
	ll 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,p[N],t,cnt,tot;
ll m,ans,a[N],b[N][2],c[N][2];
struct data{int to,nxt;ll len;
}edge[N];
void addedge(int x,int y,ll z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;}
void dfs(int k)
{
	for (int i=p[k];i;i=edge[i].nxt)
	{
		a[edge[i].to]=a[k]^edge[i].len;
		dfs(edge[i].to);
	}
}
int jump(int x,int u,int k)
{
	if (u==0) return ((a[x]>>k)&1)==0?x:-1;
	for (int i=x;i<=n;i++)
	{
		if ((a[i]>>k+1)!=(a[x]>>k+1)) return -1;
		if ((a[i]>>k)&1) return i;
	}
	return -1;
}
int query(int x,int u,int k)
{
	x=jump(x,u,k);
	if (x==-1) return 0;
	for (int i=x+1;i<=n;i++)
	if ((a[i]>>k)!=(a[x]>>k)) return i-x;
	return n-x+1;
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d\n";
#endif
	n=read(),m=read();
	for (int i=2;i<=n;i++)
	{
		int x=read();ll y=read();
		addedge(x,i,y);
	}
	dfs(1);sort(a+1,a+n+1);cnt++;b[cnt][0]=b[cnt][1]=1;
	for (int k=62;~k;k--)
	{
		ll s=0;
		for (int i=1;i<=cnt;i++)
		s+=1ll*query(b[i][0],0,k)*query(b[i][1],0,k)+1ll*query(b[i][0],1,k)*query(b[i][1],1,k);
		tot=0;ans<<=1;
		if (s>=m)
		{
			for (int i=1;i<=cnt;i++)
			{
				if (query(b[i][0],0,k)&&query(b[i][1],0,k)) tot++,c[tot][0]=jump(b[i][0],0,k),c[tot][1]=jump(b[i][1],0,k);
				if (query(b[i][0],1,k)&&query(b[i][1],1,k)) tot++,c[tot][0]=jump(b[i][0],1,k),c[tot][1]=jump(b[i][1],1,k);
			}
		}
		else
		{
			for (int i=1;i<=cnt;i++)
			{
				if (query(b[i][0],0,k)&&query(b[i][1],1,k)) tot++,c[tot][0]=jump(b[i][0],0,k),c[tot][1]=jump(b[i][1],1,k);
				if (query(b[i][0],1,k)&&query(b[i][1],0,k)) tot++,c[tot][0]=jump(b[i][0],1,k),c[tot][1]=jump(b[i][1],0,k);
			}
			m-=s;ans++;
		}
		cnt=tot;
		for (int i=1;i<=cnt;i++) b[i][0]=c[i][0],b[i][1]=c[i][1];
	}
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  G:先考虑怎么求答案是否为0。平面图上下连通相当于对偶图左右不连通,考虑以凸多边形某个点标记Bob,那么每个圆会给该点带来一个形如圆角凸多边形的不合法区域,不合法区域有交则称两圆连通,如果这些不合法区域使得左右连通,说明答案>0。回到原题,做法类似,对圆之间的连通关系(及圆和边界的连通关系)建图后,即求最小割点数量,拆点跑最小割即可。

  主要问题在于如何判断圆角凸多边形是否有交。真要带上圆角简直不可做,事实上可以转化为求两凸多边形的最近距离。对任一凸包取反后求闵可夫斯基和,看所得凸包中是否存在点到原点的距离<两圆半径之和即可。这得讨论一下原点是否在凸包中。然后我就因为这个写挂调了几天。自闭了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cassert>
using namespace std;
#define ll long long
#define int long long
#define N 410
#define S 0
#define T 401
#define in(i) (i*2-1)
#define out(i) (i*2)
#define vector point
#define p(x,y) (point){x,y}
#define O p(0,0)
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,w,L,R,p[N],d[N],q[N],cur[N],t=-1,ans;
struct point
{
	int x,y;
	vector operator +(const vector&a) const
	{
		return (vector){x+a.x,y+a.y};
	}
	vector operator -(const vector&a) const
	{
		return (vector){x-a.x,y-a.y};
	}
	int operator *(const vector&a) const
	{
		return x*a.y-y*a.x;
	}
	int operator ^(const vector&a) const
	{
		return x*a.x+y*a.y;
	}
}a[N],u[N<<1],v[N<<1],r[N<<1],Left;
struct circle{point o;int r;
}b[N];
struct data{int to,nxt,cap,flow;
}edge[N*N<<3];
void addedge(int x,int y,int z)
{
	t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,p[x]=t;
	t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,p[y]=t;
}
bool bfs()
{
	memset(d,255,sizeof(d));d[S]=0;
	int head=0,tail=1;q[1]=S;
	do
	{
		int x=q[++head];
		for (int i=p[x];~i;i=edge[i].nxt)
		if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap)
		{
			d[edge[i].to]=d[x]+1;
			q[++tail]=edge[i].to;
		}
	}while (head<tail);
	return ~d[T];
}
int work(int k,int f)
{
	if (k==T) return f;
	int used=0;
	for (int i=cur[k];~i;i=edge[i].nxt)
	if (d[k]+1==d[edge[i].to])
	{
		int w=work(edge[i].to,min(f-used,edge[i].cap-edge[i].flow));
		edge[i].flow+=w,edge[i^1].flow-=w;
		if (edge[i].flow<edge[i].cap) cur[k]=i;
		used+=w;if (used==f) return f;
	}
	if (used==0) d[k]=-1;
	return used;
}
void dinic()
{
	while (bfs())
	{
		memcpy(cur,p,sizeof(p));
		ans+=work(S,N);
	}
}
int len(vector a){return a^a;}
bool isin(point *r,int n) 
{
	for (int i=2;i<=n;i++)
	if ((r[i]-r[i-1])*(O-r[i-1])<0) return 0;
	return 1;
}
bool check(point a,point b,int r)
{
	if (((b-a)^(O-a))>0&&((a-b)^(O-b))>0)
	return ((b-a)*(O-a))*((b-a)*(O-a))<r*r*len(a-b);
	else return 0;
}
int merge(point *u,point *v)
{
	r[1]=u[1]+v[1];int tail=1;
	int i=1,j=1;
	while (i<=n||j<=n)
	{
		if (i>n) j++;
		else if (j>n) i++;
		else if ((u[i]+v[j+1]-r[tail])*(u[i+1]+v[j]-r[tail])>0) j++;
		else i++;
		point t=u[i]+v[j];
		while (tail>1&&(t-r[tail-1])*(r[tail]-r[tail-1])>0) tail--;
		r[++tail]=t;
	}
	return tail;
}
bool cross(circle x,circle y)
{
	for (int i=1;i<=n;i++) u[i]=x.o-(a[i]-Left);
	for (int i=1;i<=n;i++) v[i]=(a[i]-Left)-y.o;
	int u_first=1;
	for (int i=2;i<=n;i++) if (u[i].x<u[u_first].x||u[i].x==u[u_first].x&&u[i].y<u[u_first].y) u_first=i;
	int v_first=1;
	for (int i=2;i<=n;i++) if (v[i].x<v[v_first].x||v[i].x==v[v_first].x&&v[i].y<v[v_first].y) v_first=i;
	for (int i=n+1;i<=2*n;i++) u[i]=u[i-n],v[i]=v[i-n];
	int tail=merge(u+u_first-1,v+v_first-1);
	if (isin(r,tail)) return 1;
	for (int i=1;i<=tail;i++)
	if (len(r[i])<(x.r+y.r)*(x.r+y.r)) return 1;
	for (int i=1;i<tail;i++)
	if (check(r[i],r[i+1],x.r+y.r)) return 1;
	return 0;
}
signed main()
{                       
#ifndef ONLINE_JUDGE
	freopen("g.in","r",stdin);
	freopen("g.out","w",stdout);
	const char LL[]="%I64d\n";
#endif
	n=read(),w=read();memset(p,255,sizeof(p));
	Left=(point){N*N,N*N};L=N*N;R=-N*N;
	for (int i=1;i<=n;i++)
	{
		a[i].x=read(),a[i].y=read();
		L=min(L,a[i].x),R=max(R,a[i].x);
		if (a[i].x<Left.x) Left=a[i];
	}
	m=read();
	for (int i=1;i<=m;i++) b[i].o.x=read(),b[i].o.y=read(),b[i].r=read();
	for (int i=1;i<=m;i++)
		for (int j=i+1;j<=m;j++)
		if (cross(b[i],b[j])) addedge(out(i),in(j),1),addedge(out(j),in(i),1);
	for (int i=1;i<=m;i++)
	{
		if (b[i].o.x-(R-L)-b[i].r<0) addedge(S,in(i),1);
		if (b[i].o.x+(R-L)+b[i].r>w) addedge(out(i),T,1);
	}
	for (int i=1;i<=m;i++) addedge(in(i),out(i),1);
	dinic();
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}
posted @ 2019-02-10 17:51  Gloid  阅读(373)  评论(0编辑  收藏  举报