碎碎念
最近好颓啊,bzoj权限到期,cf又没时间打,还要忙着快乐文化课。。
翻译来自洛谷
A Roman and Browser
给定一个长度为nnn的只有111和−1−1−1的序列,选择一个位置bbb,然后删掉位置为b+i×kb+i\times kb+i×k的数(iii为整数),求操作后111和−1−1−1数量的最大绝对差值
没啥好说的,常规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(1≤ai≤m),从左往右加入题,当加入的题中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)∣a−b∣≤10−6,你的答案被算作正确。
高中生数学题。n个圆恰好组成正n边形,我们随便抽一个等腰三角形出来求出顶角,用正弦定理然后化一下柿子就可以了。
R=rsinα21−sinα2R=\frac{rsin\frac{\alpha}{2}}{1-sin\frac{\alpha}{2}}R=1−sin2α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
给定nnn和ai…na_{i\dots n}ai…n
有qqq个询问:
给定l,rl,rl,r
求在al…ra_{l\dots r}al…r中选取任意个,使得他们的异或和最大。
很容易想到线性基求异或最大值,区间问题自然想到套在线段树上解决,可惜这样是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;
}
然后就完了。

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

被折叠的 条评论
为什么被折叠?



