博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

2017-2018 ACM-ICPC, Asia Daejeon Regional Contest (E,G,H,I,K)


9.26 2017-2018 ACM-ICPC, Asia Daejeon Regional Contest

CF
vjudge


E.How Many to Be Happy?(最小割)

正解是网络流,因为范围很小...
对每条边,要使 加入所有边权小于它的边后图仍不能连通,而加入这条边后可以连通。
可以以这条边的两个端点作为源汇点,将所有边权小于它的边加入图中,跑最小割即为它的答案。
没写代码


G.Rectilinear Regions(模拟)

模拟。。就是判断一下直线相交的时候的情况。
直接判断加入一个点之前与之后两条直线的点的位置关系,再用一个变量临时存面积,区域结束时加入答案即可,但我写麻烦了。。(可以看这个
结果是不仅要判的多,遇到递减序列需要将序列反过来变成递增(注意y会变化!!)。

//31ms	500KB
#include <cstdio>
#include <cctype>
#include <cassert>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=25005,INF=1<<28;

LL Ans[N];
bool vis[50005];
struct Node
{
	int x,y;
}down[N],up[N];

inline int read()
{
	int now=0,f=1;register char c=gc();
	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now*f;
}

int main()
{
	int m=read(),n=read(),yd=read();
	for(int i=1; i<=m; ++i) down[i].x=read(),down[i].y=read(),assert(!vis[down[i].x]),vis[down[i].x]=1;
	int yu=read();
	for(int i=1; i<=n; ++i) up[i].x=read(),up[i].y=read(),assert(!vis[up[i].x]),vis[up[i].x]=1;
	if((yd>down[1].y)^(yu>up[1].y)) return puts("0 0"),0;

	int tag=0;
	if(yd>down[1].y)//zbl
	{
		std::reverse(up+1,up+1+n), std::reverse(down+1,down+1+m);
		tag=1, up[n+1].y=yu, yu=up[1].y, down[m+1].y=yd, yd=down[1].y;
		for(int i=1; i<=n; ++i) up[i].x*=-1;
		for(int i=1; i<=m; ++i) down[i].x*=-1;
	}

	int cnt=0,las=INF; up[n+1].x=INF, down[m+1].x=INF;
	for(int i=1,j=1; i<=n||j<=m; )//i:up j:down
	{
		if(up[i].x<down[j].x)
		{
			int x=up[i].x,y=up[(i++)+tag].y;
			if(yu>yd)
				if(las<INF) Ans[cnt]+=1ll*(x-las)*(yu-yd), las=x;
				else ;
			else if(y>yd) las=x, ++cnt;
			yu=y;
		}
		else
		{
			int x=down[j].x,y=down[(j++)+tag].y;
			if(yu>yd)
				if(y<yu)
					if(las<INF) Ans[cnt]+=1ll*(x-las)*(yu-yd), las=x;
					else ;
				else Ans[cnt]+=1ll*(x-las)*(yu-yd), las=INF;
			yd=y;
		}
	}
	if(las<INF) --cnt;
	LL ans=0;
	for(int i=1; i<=cnt; ans+=Ans[i++]);
	printf("%d %lld\n",cnt,ans);

	return 0;
}

H.Rock Paper Scissors(FFT) √

就是求字符串匹配的最大长度。
FFT,类似这道题,不过更简单。
分别枚举三个字符,每次将该字符在S和T中的位置设为1,两个多项式相乘即可得到这个字符在任意位置的匹配个数。

//202ms	10600KB
#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=(1<<18)+5;
const double PI=acos(-1);

int rev[N],ans[N];
char S[N],T[N];
struct Complex
{
	double x,y;
	Complex(double x=0,double y=0):x(x),y(y) {}
	Complex operator +(const Complex &a) {return Complex(x+a.x, y+a.y);}
	Complex operator -(const Complex &a) {return Complex(x-a.x, y-a.y);}
	Complex operator *(const Complex &a) {return Complex(x*a.x-y*a.y, x*a.y+y*a.x);}
}A[N],B[N];

inline int read()
{
	int now=0,f=1; char c=gc();
	for(; !isdigit(c); c=='-'&&(f=-1),c=gc());
	for(; isdigit(c); now=now*10+c-48,c=gc());
	return now*f;
}
void FFT(Complex *a,int lim,int opt)
{
	for(int i=1; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
	for(int i=2; i<=lim; i<<=1)
	{
		int mid=i>>1; Complex Wn(cos(PI/mid),opt*sin(PI/mid));
		for(int j=0; j<lim; j+=i)
		{
			Complex w(1,0),t;
			for(int k=j; k<j+mid; ++k,w=w*Wn)
				a[k+mid]=a[k]-(t=a[k+mid]*w), a[k]=a[k]+t;
		}
	}
	if(opt==-1) for(int i=0; i<lim; ++i) a[i].x/=lim;
}

int main()
{
	int n=read(),m=read();
	scanf("%s",S), scanf("%s",T);
	for(int i=0; i<m; ++i) T[i]=(T[i]=='R')?'S':T[i]=='P'?'R':'P';
	std::reverse(T,T+m);
	for(int i=m; i<n; ++i) T[i]='A';

	int lim=1,l=-1;
	while(lim<=n+m-2) lim<<=1,++l;
	for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);

	for(int i=0; i<n; ++i) A[i]=Complex(S[i]=='P'?1:0,0),B[i]=Complex(T[i]=='P'?1:0,0);
	for(int i=n; i<lim; ++i) A[i]=Complex(0,0),B[i]=Complex(0,0);
	FFT(A,lim,1), FFT(B,lim,1);
	for(int i=0; i<lim; ++i) A[i]=A[i]*B[i];
	FFT(A,lim,-1);
	for(int i=0; i<n; ++i) ans[i]+=int(A[m+i-1].x+0.5);

	for(int i=0; i<n; ++i) A[i]=Complex(S[i]=='R'?1:0,0),B[i]=Complex(T[i]=='R'?1:0,0);
	for(int i=n; i<lim; ++i) A[i]=Complex(0,0),B[i]=Complex(0,0);
	FFT(A,lim,1), FFT(B,lim,1);
	for(int i=0; i<lim; ++i) A[i]=A[i]*B[i];
	FFT(A,lim,-1);
	for(int i=0; i<n; ++i) ans[i]+=int(A[m+i-1].x+0.5);

	for(int i=0; i<n; ++i) A[i]=Complex(S[i]=='S'?1:0,0),B[i]=Complex(T[i]=='S'?1:0,0);
	for(int i=n; i<lim; ++i) A[i]=Complex(0,0),B[i]=Complex(0,0);
	FFT(A,lim,1), FFT(B,lim,1);
	for(int i=0; i<lim; ++i) A[i]=A[i]*B[i];
	FFT(A,lim,-1);
	for(int i=0; i<n; ++i) ans[i]+=int(A[m+i-1].x+0.5);

	int res=0;
	for(int i=0; i<n; ++i) res=std::max(res,ans[i]);
	printf("%d\n",res);

	return 0;
}

I.Slot Machines(KMP) √

将串反过来,然后如果一个位置是一个循环节(前缀是该循环节)的结尾,就可以作为答案,暴力枚举该循环节长度更新答案即可。
复杂度应该是2n叭。

//62ms	7800KB
#include <bits/stdc++.h>
#define gc() getchar()
typedef long long LL;
const int N=1e6+5;

int f[N],A[N];

inline int read()
{
	int now=0,f=1; char c=gc();
	for(; !isdigit(c); c=='-'&&(f=-1),c=gc());
	for(; isdigit(c); now=now*10+c-48,c=gc());
	return now*f;
}

int main()
{
	int n=read();
	for(int i=1; i<=n; ++i) A[i]=read();
	std::reverse(A+1,A+1+n);
	for(int i=2,j=0; i<=n; ++i)
	{
		while(j&&A[i]!=A[j+1]) j=f[j];
		f[i]=A[i]==A[j+1]?++j:0;
	}
	int k=1e9,p=1e9;
	for(int i=1; i<=n; ++i)
		if(!(i%(i-f[i])))
		{
			int kk=n-i,pp=i-f[i];
			for(int j=i+1; j<i+i-f[i]; ++j)
				if(A[j]==A[j-i]) --kk;
				else break;
			if(kk+pp<k+p) k=kk, p=pp;
		}
	printf("%d %d\n",k,p);

	return 0;
}

K.Untangling Chain(模拟) √

可以发现绕着起点不断转圈即可。
转圈的话需要记当前走过的,最左上、左下、右上、右下位置,下次走时越过极限位置即可。
边长最长(最差)的情况下是走一条直线,恰好需要走n步。

//30ms	100KB
#include <bits/stdc++.h>
#define gc() getchar()
typedef long long LL;
const int N=10005;

struct Opt
{
	int len,way;
}opt[N];
struct Point
{
	int x,y;
};

inline int read()
{
	int now=0,f=1; char c=gc();
	for(; !isdigit(c); c=='-'&&(f=-1),c=gc());
	for(; isdigit(c); now=now*10+c-48,c=gc());
	return now*f;
}

int main()
{
	int n=read();
	for(int i=1; i<=n; ++i) opt[i]=(Opt){read(),read()};
	int x=0,y=0,now=2;//now:朝向 0123:左上右下 
	Point ul=(Point){0,0},ur=ul,dl=ul,dr=ul;
	for(int i=1; i<=n; ++i)
	{
		switch(now)
		{
			case 0:
			{
				int d=std::abs(x-std::min(ul.x,dl.x))+1;
				x-=d, printf("%d ",d);
				break;
			}
			case 1:
			{
				int d=std::abs(std::max(ul.y,ur.y)-y)+1;
				y+=d, printf("%d ",d);
				break;
			}
			case 2:
			{
				int d=std::abs(std::max(ur.x,dr.x)-x)+1;
				x+=d, printf("%d ",d);
				break;
			}
			case 3:
			{
				int d=std::abs(y-std::min(dl.y,dr.y))+1;
				y-=d, printf("%d ",d);
				break;
			}
		}
		if(x<=ul.x && y>=ul.y) ul=(Point){x,y};
		if(x>=ur.x && y>=ur.y) ur=(Point){x,y};
		if(x<=dl.x && y<=dl.y) dl=(Point){x,y};
		if(x>=dr.x && y<=dr.y) dr=(Point){x,y};
		now=((now-opt[i].way)%4+4)%4;
	}
	

	return 0;
}
posted @ 2020-09-26 18:23  SovietPower  阅读(196)  评论(1编辑  收藏  举报