Codeforces Round #532 (Div. 2) 题解

本文精选了多项算法竞赛题目,包括模拟、线段树、拓扑排序、线性基等高级算法的应用,详细解析了从简单到复杂的题解思路,为算法爱好者提供了丰富的实战案例。

碎碎念


最近好颓啊,bzoj权限到期,cf又没时间打,还要忙着快乐文化课。。

翻译来自洛谷

A Roman and Browser


给定一个长度为nnn的只有111−1−11的序列,选择一个位置bbb,然后删掉位置为b+i×kb+i\times kb+i×k的数(iii为整数),求操作后111−1−11数量的最大绝对差值


没啥好说的,常规A题难度,直接模拟即可


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=20005;

int a[N];

int main(void) {
	int n,k,sum=0,mx=0; scanf("%d%d",&n,&k);
	rep(i,1,n) scanf("%d",&a[i]),sum+=a[i];
	rep(i,1,k) {
		int s=sum;
		for (int j=i;j<=n;j+=k) s-=a[j];
		mx=std:: max(mx,abs(s));
	}
	printf("%d\n",mx);
	return 0;
}

B Build a Contest


nnn个题,每个题有一个难度ai(1≤ai≤m)a_i(1\le a_i\le m)ai(1aim),从左往右加入题,当加入的题中mmm个难度都出现时,输出111并把每个难度都删除一道题,否则输出000,求输出序列


我们线段树单点修改+查询全局最小值,实在无脑直接再上一个区间减即可


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=200005;

int s[N<<2];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void mdf(int x,int l,int r,int v) {
	if (v<l||v>r) return ;
	if (l==r) return (void) (s[x]++);
	int md=(l+r)>>1;
	mdf(x<<1,l,md,v); mdf(x<<1|1,md+1,r,v);
	s[x]=std:: min(s[x<<1],s[x<<1|1]);
}

int main(void) {
	int m=read(),n=read(),rec=1;
	rep(i,1,n) {
		int x=read();
		mdf(1,1,m,x);
		if (s[1]>=rec) {
			putchar('1');
			rec++;
		} else putchar('0');
	}
	return 0;
}

C NN and the Optical Illusion


有一个圆,在其周围摆一圈圆,如图所示:
在这里插入图片描述
现已知内圆半径rrr,和外圆个数nnn,你要求出外圆半径RRR
假设你输出的答案是aaa,标准答案为bbb,如果∣a−b∣max⁡(1,b)≤10−6\frac{|a-b|}{\max(1,b)}\le10^{-6}max(1,b)ab106,你的答案被算作正确。


高中生数学题。n个圆恰好组成正n边形,我们随便抽一个等腰三角形出来求出顶角,用正弦定理然后化一下柿子就可以了。
R=rsinα21−sinα2R=\frac{rsin\frac{\alpha}{2}}{1-sin\frac{\alpha}{2}}R=1sin2αrsin2α
其中r是内圆半径,R是外圆半径


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>

const double pi=acos(-1);

int main(void) {
	int n,r; scanf("%d%d",&n,&r);
	double arc=2.0*pi/n;
	double sn=sin(arc*0.5);
	printf("%.7lf\n", r*sn/(1-sn));
}

D Dasha and Chess


999×999999×999999×999的棋盘上,有一个白王和666666666个黑车,每次白王可以往888个方向走一格,黑车可以随便移动到某个没有棋子的位置,当黑车移动后白王和某个黑车在同一行/列时白王赢,求白王在200020002000步之内的必胜策略


脑洞是好东西,我也想要一个
我们首先走到最中间(500,500)(500,500)(500,500),最坏的情况为四个角落分别有(166,166,167,167)个车,我们只需要向着车最多的角落走斜线就可以了
至于正确性,我们从(500,500)(500,500)(500,500)到任意角落只需要499499499步,而角落车的数量至少有166+167+167=500166+167+167=500166+167+167=500只。
实现的时候注意只能走空格。。


嘴巴AC没有代码

E Andrew and Taxi


给定一个有向图,改变其中某些边的方向,它将成为一个有向无环图。
现在求一个改变边方向的方案,使得所选边边权的最大值最小。


首先答案肯定是单调的。考虑二分答案mid,我们把大于mid的边都加图里判环,若出现环了肯定不可行
考虑没有环的情况。我们对新图做拓扑排序,需要修改的边一定是逆着拓扑序连接的。我们把这些边反向就可以惹


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const int N=200005;

struct edge {int x,y,w,next;} e[N],g[N];

std:: vector <int> ans;

int ls[N],d[N],edCnt;
int q[N],p[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y,int w) {
	e[++edCnt]=(edge) {x,y,w,ls[x]}; d[y]++; ls[x]=edCnt;
}

bool cmp(edge a,edge b) {
	return a.w<b.w;
}

bool top_sort(int n) {
	int h=1,t=0;
	rep(i,1,n) if (!d[i]) q[++t]=i;
	for (;h<=t;) {
		int x=q[h++]; p[x]=h-1;
		for (int i=ls[x];i;i=e[i].next) {
			if (!(--d[e[i].y])) {
				q[++t]=e[i].y;
			}
		}
	}
	return t==n;
}

bool check(int mid,int n,int m) {
	rep(i,0,n) d[i]=ls[i]=0; edCnt=0;
	rep(i,mid+1,m) add_edge(g[i].x,g[i].y,g[i].y);
	return top_sort(n);
}

int main(void) {
	freopen("data.in","r",stdin);
	int n=read(),m=read();
	rep(i,1,m) {
		int x=read(),y=read(),w=read();
		g[i]=(edge) {x,y,w,i};
	}
	std:: sort(g+1,g+m+1,cmp);
	if (check(0,n,m)) {
		puts("0 0");
		return 0;
	}
	int l=1,r=m;
	for (;l<=r;) {
		int mid=(l+r)>>1;
		if (check(mid,n,m)) {
			r=mid-1; ans.clear();
			rep(i,1,mid) if (p[g[i].x]>p[g[i].y]) {
				ans.push_back(g[i].next);
			}
		}
		else l=mid+1;
	}
	printf("%d %d\n", g[r+1].w,(int)ans.size());
	for (int i=0;i<ans.size();++i) printf("%d ", ans[i]); puts("");
	return 0;
}

CF1100F Ivan and Burgers


给定nnnai…na_{i\dots n}ain
qqq个询问:
给定l,rl,rl,r
求在al…ra_{l\dots r}alr​中选取任意个,使得他们的异或和最大。


很容易想到线性基求异或最大值,区间问题自然想到套在线段树上解决,可惜这样是O(nlog3n)O(nlog^3n)O(nlog3n)
我们离线做。对于跨过mid的询问,维护从mid开始的前缀和后缀线性基,然后合并查询就可以了。对于不跨过的分治即可
分析一下复杂度是O((n+q)log2n)O((n+q)log^2n)O((n+q)log2n)的,区别在于只有枚举q时合并了线性基,其余时间都只有插入的操作。
实际上还是可以做到一个log的,具体可以看洛谷的题解


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define drp(i,st,ed) for (register int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))

const int N=500005;

struct Gay {
	int r[21],size;
	Gay() { fill(r,0),size=0;}
	void clr() { fill(r,0),size=0;}
	void ins(int x) {
		drp(i,19,0) if ((x>>i)&1) {
			if (!r[i]) {
				size++; r[i]=x;
				return ;
			} else x^=r[i];
		}
	}
	int get_max() {
		int res=0;
		drp(i,19,0) if ((res^r[i])>res) res^=r[i];
		return res;
	}
} G[N<<2],wjp;

struct Q {int l,r,id;} q[N];

int ans[N],a[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void solve(int l,int r,int L,int R) {
	if (R<L) return ;
	int mid=(l+r)>>1;
	G[mid].clr(); G[mid].ins(a[mid]);
	rep(i,mid+1,r) {
		G[i]=G[i-1];
		G[i].ins(a[i]);
	}
	drp(i,mid-1,l) {
		G[i]=G[i+1];
		G[i].ins(a[i]);
	}
	std:: vector <Q> v1,v2;
	rep(i,L,R) if (q[i].r<mid) v1.push_back(q[i]);
	else if (q[i].l>mid) v2.push_back(q[i]);
	else {
		wjp=G[q[i].l];
		rep(j,0,19) wjp.ins(G[q[i].r].r[j]);
		ans[q[i].id]=wjp.get_max();
	}
	int cnt=0,rec;
	for (int i=0;i<v1.size();++i) q[L+cnt++]=v1[i];
	rec=L+cnt;
	for (int i=0;i<v2.size();++i) q[L+cnt++]=v2[i];
	solve(l,mid,L,rec-1);
	solve(mid+1,r,rec,L+cnt-1);
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read();
	rep(i,1,n) a[i]=read();
	int T=read();
	rep(i,1,T) q[i].l=read(),q[i].r=read(),q[i].id=i;
	solve(1,n,1,T);
	rep(i,1,T) printf("%d\n", ans[i]);
	return 0;
}

然后就完了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值