【泛刷题】gym101471 world final 2017 (G~L)

【前言】
没错这里是下篇。
上篇请移步这里

【题目】
gym

G.Replicate Replicate Rfplicbte

一个无限的网格图,初始某些格子内有细胞,每个时刻若一个格子周围八个加上它自己中有奇数个格子有细胞则这个格子下个时刻会有细胞,否则没有。现在某些时刻出现了一个 bug \text{bug} bug,即每次更新状态后,有一个格子的状态可能发生变化。

现在给定最终的网格图边框大小,问最开始可能的最小非空网格图是什么。 n , m ≤ 300 n,m\leq 300 n,m300

观察到不论是否有 bug \text{bug} bug,细胞方阵至少向四个方向拓展一个,所以总步数是 O ( n ) O(n) O(n)的,假设没有 bug \text{bug} bug,我们不难逐行倒推出每个格子的状态(我们知道这个格子左上的格子状态以及其周围状态),若在第 ( x , y ) (x,y) (x,y)出现了一个 bug \text{bug} bug,那么它对第 x + 1 x+1 x+1行的影响是使得 ( r + 1 , c + 3 k − 1 ) ( r + 1 , c + 3 k − 2 ) (r+1,c+3k-1)(r+1,c+3k-2) (r+1,c+3k1)(r+1,c+3k2)状态改变,其中 k k k是正整数。

因此当我们倒推到某行时若发现超出边界的两个格子中有一个是非空的,说明这行中出现了 bug \text{bug} bug,此时我们再按列进行递推,找出出现 bug \text{bug} bug的列。这样我们找到了发生 bug \text{bug} bug位置,还原后继续递推即可。

复杂度 O ( ( n + m ) 3 ) O((n+m)^3) O((n+m)3)

有点不敢写,抄了一遍。

#include
using namespace std;

const int N=305;
int n,m,f[N][N],g[N][N];
char ch[N];

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;++j) f[i][j]=ch[j]=='#';
	}
	int lx=0,ly=0;
	while(n>2 && m>2) 
	{
		//puts("");
		int px=0,py=0;
		for(int i=2;i<n && !px;++i) 
		{
			g[i][2]=f[i-1][1]^g[i-2][2]^g[i-1][2];
			for(int j=3;j<m;++j) 
				g[i][j]=f[i-1][j-2]^f[i-1][j-1]^g[i-2][j-3]^g[i-2][j]^g[i-1][j-3]^g[i-1][j]^g[i][j-3];
			if((f[i-1][m-2]^f[i-1][m-1]^g[i-2][m-3]^g[i-1][m-3]^g[i][m-3]) || (f[i-1][m-1]^f[i-1][m]^g[i-2][m-2]^g[i-1][m-2]^g[i][m-2]))
				px=i-1;
		}
		//printf("%d ",px);
		if(!px)//g[n][i]=0
		{
			if(f[n-1][1]^g[n-2][2]^g[n-1][2]) px=n-1;
			for(int i=3;i<m && !px;++i)
				if(f[n-1][i-2]^f[n-1][i-1]^g[n-2][i-3]^g[n-2][i]^g[n-1][i-3]^g[n-1][i])
					px=n-1;
			if((f[n-1][m-2]^f[n-1][m-1]^g[n-2][m-3]^g[n-1][m-3]) || (f[n-1][m-1]^f[n-1][m]^g[n-2][m-2]^g[n-1][m-2]))
				px=n-1;//g[n][m]=g[n][m+1]=0
		}
		//printf("%d ",px);
		if(!px)//g[n+1][i]=0
		{
			if(f[n][1]^g[n-1][2]) px=n;
			for(int i=3;i<m && !px;++i) 
				if(f[n][i-2]^f[n][i-1]^g[n-1][i-3]^g[n-1][i]) 
					px=n;
			if((f[n][m-2]^f[n][m-1]^g[n-1][m-3]) || (f[n][m-1]^f[n][m]^g[n-1][m-2])) 
				px=n;
		}
		//printf("%d ",px);
		if(px)
		{
			if(lx && ly) {f[lx][ly]^=1;break;}
			for(int i=2;i<m && !py;++i)
			{
				g[2][i]=f[1][i-1]^g[2][i-2]^g[2][i-1];
				for(int j=3;j<n;++j)
					g[j][i]=f[j-2][i-1]^f[j-1][i-1]^g[j-3][i-2]^g[j-3][i-1]^g[j-3][i]^g[j][i-2]^g[j][i-1];
				if((f[n-2][i-1]^f[n-1][i-1]^g[n-3][i-2]^g[n-3][i-1]^g[n-3][i]) || (f[n-1][i-1]^f[n][i-1]^g[n-2][i-2]^g[n-2][i-1]^g[n-2][i]))
					py=i-1;
			}
			//printf("%d ",py);
			if(!py)//g[i][m]=0
			{
				if(f[1][m-1]^g[2][m-2]^g[2][m-1]) py=m-1;
				for(int i=3;i<n && !py;++i)
					if(f[i-2][m-1]^f[i-1][m-1]^g[i-3][m-2]^g[i-3][m-1]^g[i][m-2]^g[i][m-1])
						py=m-1;
				if((f[n-2][m-1]^f[n-1][m-1]^g[n-3][m-2]^g[n-3][m-1]) || (f[n-1][m-1]^f[n][m-1]^g[n-2][m-2]^g[n-2][m-1]))
					py=m-1;
			}
			//printf("%d ",py);
			if(!py)//g[i][m+1]=0
			{
				if(f[1][m]^g[2][m-1]) py=m;
				for(int i=3;i<n && !py;++i)
					if(f[i-2][m]^f[i-1][m]^g[i-3][m-1]^g[i][m-1])
						py=m;
				if((f[n-2][m]^f[n-1][m]^g[n-3][m-1]) || (f[n-1][m]^f[n][m]^g[n-2][m-1]))
					py=m;
			}
			//printf("%d ",py);
			if(py)
			{
				int cnt=0;
				for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) cnt+=f[i][j];
				if(cnt==1) break;
				lx=px;ly=py;f[px][py]^=1;
				continue;
			}
		}
		lx=ly=0;n-=2;m-=2;
		for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) f[i][j]=g[i+1][j+1];
	}
	//puts("");
	int xl=n,xr=1,yl=m,yr=1;
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
		if(f[i][j]) xl=min(xl,i),xr=max(xr,i),yl=min(yl,j),yr=max(yr,j);
	for(int i=xl;i<=xr;++i) 
	{
		for(int j=yl;j<=yr;++j) ch[j-yl]=f[i][j]?'#':'.';
		ch[yr-yl+1]='\0'; puts(ch);
	}
	return 0;
}

H.Scenery

n n n张照片,每张照片有一个适宜拍摄时间 ( s i , t i ) (s_i,t_i) (si,ti),拍摄一张照片需要 K K K的时间,问是否能拍完所有照片。
n ≤ 1 0 4 , K ≤ 1 0 5 , s i , t i ≤ 1 0 9 n\leq 10^4,K\leq 10^5,s_i,t_i\leq 10^9 n104,K105,si,ti109

看上去是个贪心?然而我贪不动啊,官方题解英文看不懂。于是我们看L_0_Forever_LF的题解:

我们考虑一种 naive \text{naive} naive的贪心,即每次照当前能照的结束时间最早的。然而这样显然会错:譬如我们现在有一堆照片,最早能在 s s s开始处理,最迟要在 t t t开始处理,每张耗时 k k k,那么我们不能在 ( t − k , s ) (t-k,s) (tk,s)这段时间内处理任何其他一张照片,否则一定会有相片拍不完。

但实际上我们就是要处理出所有这些形如 ( t − k , s ) (t-k,s) (tk,s)的时间段,在考虑这些时间段不拍摄的情况下进行贪心。

记录 l , r l,r l,r为一张相片最早/晚开拍时间,我们显然可以 2 n 2^n 2n TLE \text{TLE} TLE的情况下枚举所有的集合,虽然不能接受就是了。不过注意到很多集合的 l m i n , r m a x l_{min},r_{max} lmin,rmax是相同的,记 s = l m i n , t = r m a x s=l_{min},t=r_{max} s=lmin,t=rmax,则区间 [ s , t ] [s,t] [s,t]中包含的相片越多,对于上面不能拍摄时间段的限制就越强,因此我们只需要考虑被 [ s , t ] [s,t] [s,t]完全包含的所有相片这个集合。

于是考虑对于每个区间 [ s , t ] [s,t] [s,t],对于被其完全包含的所有照片,我们无视它们的限制,从后往前贪心放(考虑不能放的区间情况下),求出最晚在 C C C时刻拍这个区间内的第一张照片。

C < s C<s C<s则无解,否则当 C < s + t C<s+t C<s+t时, ( C − t , s ) (C-t,s) (Ct,s)这个区间内不能拍照。

接下来对每个 s s s我们维护一个最小的 C − t C-t Ct,按 s s s降序枚举区间, [ s , t ] [s,t] [s,t] C C C可以由 [ s + 1 , t ] [s+1,t] [s+1,t] C C C递推过来,若将所有 [ s , t ] [s,t] [s,t]都推晚仍没有无解就一定有解,因为 [ s , t ] [s,t] [s,t]包含所有相片的这个区间,其 C ≥ s C\ge s Cs本身就包含了 n n n个相片合法这一条件。

复杂度 O ( n 2 ) O(n^2) O(n2)

#include
#define fi first
#define se second
#define mkp make_pair
using namespace std;

const int N=1e4+10,INF=0x3f3f3f3f;
int n,m,tot,s;
int q[N],b[N],nex[N];
pair<int,int> a[N],seg[N];

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;++i) scanf("%d%d",&a[i].fi,&a[i].se),q[i]=a[i].se;
	sort(a,a+n);sort(q,q+n);tot=unique(q,q+n)-q;
	memcpy(b,q,sizeof(q));
	int pre=INF,cur=INF,low=tot,pos;
	for(int i=n-1;~i;pre=cur)
	{
		for(pos=a[i].fi;low && pos+m<=q[low-1];)
			for(--low;nex[low]<s && b[low]<=seg[nex[low]].fi;++nex[low]);
		for(;~i && a[i].fi==pos;--i)
			for(int j=lower_bound(q+low,q+tot,a[i].se)-q;j<tot;++j)
			{
				for(b[j]-=m;nex[j]<s && b[j]<=seg[nex[j]].fi;++nex[j]);
				if(nex[j]<s && b[j]<seg[nex[j]].se) b[j]=seg[nex[j]].fi;
			}
		for(int j=low;j<tot;++j) cur=min(cur,b[j]);
		if(cur<pos) {puts("no");return 0;}
		if(cur<pre && cur-m<pos) seg[s++]=mkp(cur-m,pos);
	}
	puts("yes");
	return 0;
}

I.Secret Chamber at Mount Rushmore

m m m种字母转换规则, n n n对字符串,询问第一个是否能通过任意次转换变成第二个。
m ≤ 500 , n ≤ 50 , ∣ S ∣ ≤ 50 m\leq 500,n\leq 50,|S|\leq 50 m500,n50,S50

emmm,传递闭包?

#include
using namespace std;

const int N=26;
int n,m;
bool mp[N][N];
char s[N<<1],t[N<<1];

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%s%s",s,t),mp[s[0]-'a'][t[0]-'a']=1;
	for(int i=0;i<N;++i) mp[i][i]=1;
	for(int k=0;k<N;++k) for(int i=0;i<N;++i) for(int j=0;j<N;++j)
		mp[i][j]|=mp[i][k]&&mp[k][j];
	while(m--)
	{
		scanf("%s%s",s,t);bool fg=strlen(s)==strlen(t);
		for(int i=0;i<(int)strlen(s);++i) fg&=mp[s[i]-'a'][t[i]-'a'];
		puts(fg?"yes":"no");
	}
	return 0;
}

J.Son of Pipe Stream

一幅图,有水和 F l u b b e r Flubber Flubber(下称 F F F)两种液体。要求除起点(有两个,分别是水和 F F F)和终点每个节点满足流量平衡,管道满足 v ∗ f + w ≤ c v*f+w\leq c vf+wc,其中 c c c是管道容量, v v v是一个常数, f f f F F F的流量, w w w是水的流量。求网络的最大 X a ∗ Y 1 − a X^a*Y^{1-a} XaY1a,其中 X X X表示终点流入 F F F的速率, Y Y Y表示终点流入水的速率。
n ≤ 300 , m ≤ n ( n + 1 ) 2 , v ≤ 10 , 0.01 ≤ a ≤ 0.99 , 1 ≤ c ≤ 10 n\leq 300,m\leq \frac {n(n+1)} {2},v\leq 10,0.01\leq a\leq 0.99,1\leq c\leq 10 n300,m2n(n+1),v10,0.01a0.991c10

首先这个 v v v是没有用的,我们可以将 F F F的流量乘上 v v v,最后算答案的时候再除以 v a v^a va

分别以两个出发点为源跑一次最大流,可以得到两种液体的最大流量 F m , W m F_m,W_m Fm,Wm,再新建超级源连向两个起点,可以得到两种液体混合最大流量 Z Z Z,最优的 F F F实际上就是在 [ Z − W m , F m ] [Z-W_m,F_m] [ZWm,Fm]里最贴近 a Z aZ aZ的值,那么现在可以得到新的 F ′ F' F,同时 W ′ = Z − F ′ W'=Z-F' W=ZF。证明???

#include
using namespace std;

typedef double db;
const db eps=1e-10;
const int N=205,M=4e5+10,inf=0x3f3f3f3f;
int n,m;
db F,W,Z,ans,V,alpha;
db val[M],A[M],B[M];

struct data{int u,v,w;}E[M];

namespace Flow
{
	int tot,S,T;
	int head[N],cur[N],dis[N];
	queue<int>q;
	struct Tway{int v,nex;db w;}e[M];
	void add(int u,int v,db w)
	{
		e[++tot]=(Tway){v,head[u],w};head[u]=tot;
		e[++tot]=(Tway){u,head[v],0};head[v]=tot;
	}
	bool bfs()
	{
		memcpy(cur,head,sizeof(head));
		memset(dis,-1,sizeof(dis));dis[S]=0;q.push(S);
		while(!q.empty())
		{
			int x=q.front();q.pop();
			for(int i=head[x];i;i=e[i].nex)
			{
				int v=e[i].v;
				if(!~dis[v] && e[i].w>eps) dis[v]=dis[x]+1,q.push(v);
			}
		}
		return ~dis[T];
	}
	db dfs(int x,db flow)
	{
		if(x==T || flow<eps) return flow;
		db used=0,u;
		for(int &i=cur[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(dis[v]==dis[x]+1 && (u=dfs(v,min(flow-used,e[i].w)))>eps)
			{
				e[i].w-=u;e[i^1].w+=u;used+=u;
				if(used==flow) break;
			}
		}
		return used;
	}
	db dinic()
	{
		db res=0;
		while(bfs()) res+=dfs(S,inf);
		return res;
	}
	void init(int ss,int tt){S=ss;T=tt;tot=1;memset(head,0,sizeof(head));}
}
using namespace Flow;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d%lf%lf",&n,&m,&V,&alpha);
	for(int i=1;i<=m;++i) scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
	init(1,3);for(int i=1;i<=m;++i) add(E[i].u,E[i].v,E[i].w),add(E[i].v,E[i].u,E[i].w);F=dinic();

	init(2,3);for(int i=1;i<=m;++i) add(E[i].u,E[i].v,E[i].w),add(E[i].v,E[i].u,E[i].w);W=dinic();

	init(n+1,3);for(int i=1;i<=m;++i) add(E[i].u,E[i].v,E[i].w),add(E[i].v,E[i].u,E[i].w);
	add(n+1,1,inf);add(n+1,2,inf);Z=dinic();

	F=min(F,max(Z-W,alpha*Z));W=Z-F;
	init(n+1,3);for(int i=1;i<=m;++i) add(E[i].u,E[i].v,E[i].w),add(E[i].v,E[i].u,E[i].w);
	add(n+1,1,F);add(n+1,2,W);dinic();

	for(int i=1;i<=m;++i) val[i]=e[(i<<2)-1].w-e[(i<<2)+1].w;
	init(n+1,3);for(int i=1;i<=m;++i) val[i]>0?add(E[i].u,E[i].v,val[i]):add(E[i].v,E[i].u,-val[i]);
	add(n+1,1,F);dinic();

	for(int i=1;i<=m;++i) A[i]=e[i<<1|1].w*(val[i]>0?1:-1);

	init(n+1,3);for(int i=1;i<=m;++i) val[i]>0?add(E[i].u,E[i].v,val[i]-A[i]):add(E[i].v,E[i].u,A[i]-val[i]);
	add(n+1,2,W);dinic();

	for(int i=1;i<=m;++i) B[i]=e[i<<1|1].w*(val[i]>0?1:-1);

	ans=pow(F,alpha)*pow(W,1.0-alpha)/pow(V,alpha);
	for(int i=1;i<=m;++i) printf("%.8lf %.8lf\n",A[i]/V,B[i]);
	printf("%.8lf\n",ans);
	return 0;
}

K.Tarot Sham Boast

石头剪刀布进行 n n n轮,每轮随机出,有 m m m种预测序列,将这 m m m种序列按出现概率从大到小输出。
n ≤ 1 0 6 , m ≤ 10 n\leq 10^6,m\leq 10 n106,m10,序列长度 l ≤ 1 0 5 l\leq 10^5 l105

神仙题。

结论是找出每个串长度 ≥ 2 l − n \ge 2l-n 2ln的周期(数值),将它们降序排列,这个排列的字典序越小出现在大串中的概率越大???

KMP \text{KMP} KMP可以做到复杂度 O ( l s log ⁡ s ) O(ls\log s) O(lslogs)

#include
using namespace std;

const int N=1e5+10,M=12;
int n,m,ord[M],fail[N],border[M][N];
char ch[M][N];

bool cmp(int x,int y)
{
	int t=max(border[x][0],border[y][0]);
	for(int i=1;i<=t;++i) if(border[x][i]^border[y][i])
		return border[x][i]<border[y][i];
	return x<y;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		ord[i]=i;scanf("%s",ch[i]);int len=strlen(ch[i]);
		for(int j=1,k=0;j<len;++j)
		{
			for(;k && ch[i][j]^ch[i][k];k=fail[k]);
			fail[j+1]=ch[i][j]==ch[i][k]?++k:0;
		}
		for(int j=fail[len];j;j=fail[j]) if((len<<1)-j<=n)
			border[i][++border[i][0]]=j;
	}
	sort(ord+1,ord+m+1,cmp);
	for(int i=1;i<=m;++i) printf("%s\n",ch[ord[i]]);
	return 0;
}

L.Visual Python++

给定 n n n个左上角和 n n n个右下角,将它们一一对应,使得组成的矩形无交(可以在内部,但边界也不能有交)。
n ≤ 1 0 5 n\leq 10^5 n105

显然匹配是唯一的,我们不妨用平衡树( set \text{set} set)将匹配处理出来,接着横纵各做一次扫描线判断是否合法即可。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include
#define fi first
#define se second
#define mkp make_pair
#define lowbit(x) (x&(-x))
using namespace std;

typedef pair<int,int> pii;
const int N=1e5+10,M=N<<2;
int n,qtot,etot;
int ord[N],q[M],cnt[M];
set<pii>st;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct data
{
	int x,y,id;
	void in(int i){x=read();y=read();id=i;}
}a[N],b[N];
bool cmp(const data&a,const data&b){return a.x==b.x?a.y<b.y:a.x<b.x;}
bool cmpid(const data&a,const data&b){return a.id<b.id;}
bool cmpleq(const data&a,const data&b){return a.x<b.x || (a.x<=b.x && a.y<=b.y);}

struct event
{
	int x,yl,yr,typ;
	bool operator <(event const&rhs)const{return x==rhs.x?typ>rhs.typ:x<rhs.x;}
}e[M];

struct BIT
{
	int c[M];
	void clear(){memset(c,0,sizeof(c));}
	void update(int x,int v){for(;x<M;x+=lowbit(x))c[x]+=v;}
	int query(int x){int res=0;for(;x>0;x-=lowbit(x))res+=c[x];return res;}
}bit;

bool check()
{
	qtot=etot=0;
	for(int i=1;i<=n;++i) q[++qtot]=a[i].y,q[++qtot]=b[i].y;
	sort(q+1,q+qtot+1);qtot=unique(q+1,q+qtot+1)-q-1;
	for(int i=1;i<=n;++i)
	{
		int yl=lower_bound(q+1,q+qtot+1,a[i].y)-q,yr=lower_bound(q+1,q+qtot+1,b[ord[i]].y)-q;
		e[++etot]=(event){a[i].x,yl,yr,1};e[++etot]=(event){b[ord[i]].x,yl,yr,-1};
	}
	sort(e+1,e+etot+1);bit.clear();
	for(int i=1;i<=etot;++i)
	{
		if(e[i].typ==1)
		{
			if(!cnt[e[i].yl] && !cnt[e[i].yr] && bit.query(e[i].yl-1)==bit.query(e[i].yr))
			{
				++cnt[e[i].yl];bit.update(e[i].yl,1);
				++cnt[e[i].yr];bit.update(e[i].yr,-1);
			}
			else return 0;
		}
		else
		{
			--cnt[e[i].yl];bit.update(e[i].yl,-1);
			--cnt[e[i].yr];bit.update(e[i].yr,1);
		}
	}
	return 1;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	n=read();
	for(int i=1;i<=n;++i) a[i].in(i);
	for(int i=1;i<=n;++i) b[i].in(i);
	sort(a+1,a+n+1,cmp);sort(b+1,b+n+1,cmp);
	bool fg=1;
	for(int i=n,j=n;i;--i)
	{
		for(;j && cmpleq(a[i],b[j]);--j) st.insert(mkp(b[j].y,b[j].id));
		set<pii>::iterator it=st.lower_bound(mkp(a[i].y,0));
		if(it==st.end()){fg=0;break;}
		ord[a[i].id]=it->se;st.erase(it);
	}
	sort(a+1,a+n+1,cmpid);sort(b+1,b+n+1,cmpid);
	if(fg)
	{
		fg=check();
		if(fg){for(int i=1;i<=n;++i) swap(a[i].x,a[i].y),swap(b[i].x,b[i].y);}
		fg=check();
	}
	if(fg) for(int i=1;i<=n;++i) printf("%d\n",ord[i]);
	else puts("syntax error");
	return 0;
}

【总结】
好多不会啊,自己还是太弱了。

你可能感兴趣的:(FAKE-ACM)