前言
AB都是小数据暴力直接过,C题自己开始方向偏了,改改就过掉了。整体没有拉开差距的一场,第四题想出做法奈何已经太晚,由于写法过于暴力导致一系列的错误,debug到凌晨四点,这真的是最难受的,这场得到的教训是不要轻易放弃呀,赛后补DEF,都是可以做的,下次一定要多开题。
biubiubiu_
r
a
t
i
n
g
+
=
7
rating+=7
rating+=7 1926->1933
A. Roman and Browser
题意
有一本n页的数,你可以选择从b开始,每k页撕去一页。每页数有两种类型,现在选择一个b,使得剩下的书两种类型的页数差的最多。
2
<
=
k
<
n
<
=
100
2<=k<n<=100
2<=k<n<=100
做法
n,k都是100,暴力枚举b就可以了,要注意枚举范围。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 105;
int f[maxn];
int main()
{
int n,k;
int cnt=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&f[i]);
for(int i=1;i<=n;i++)
{
if(f[i]==1) cnt++;
}
int maxx=0;
int cnt2=n-cnt;
for(int i=1;i<=n;i++)
{
int a=cnt;
int b=cnt2;
for(int j=1;j<=n;j++)
{
if((abs(j-i))%k==0)
{
if(f[j]==1) a--;
else b--;
}
}
maxx=max(maxx,abs(a-b));
}
printf("%d\n",maxx);
return 0;
}
B. Build a Contest
题意
你现在按顺序出m道问题,每场比赛要用n道难度不同的题,难度分别是1-n,当你拥有n种难度的问题,你就要用掉这n道题办一场比赛,现在问每道题加进来之后,是否在这个时刻办比赛
1
<
=
n
,
m
<
=
1
0
5
1<=n,m<=10^5
1<=n,m<=105
做法
这个题要冷静的分析一波复杂度,之后你就会发现
如果比赛题目足够之后暴力删除的话,最多删除m/n次,每次删除的复杂度是O(n)
所以这道题最暴力的做法复杂度其实是O(m),所以只需要能快速判断什么时候题目个数足够就可以。
这里我用是set的方法判断题目个数是否等于n。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
const int maxn = 1e5+5;
set<int> s;
int sum[maxn];
int ans[maxn];
int main()
{
int n,m,x;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
s.insert(x);
sum[x]++;
if(s.size()==n)
{
printf("1");
s.clear();
for(int j=1;j<=n;j++)
{
sum[j]--;
if(sum[j]>0) s.insert(j);
}
}
else
{
printf("0");
}
}
return 0;
}
C. NN and the Optical Illusion
题意
给你一个如图所示的多圆绕一圆,问小圆半径
做法
对于任意一个小圆,有下面这张图,所以直接用sin函数计算即可。
代码
#include<stdio.h>
#include<math.h>
const double PI = acos(-1);
int main()
{
double n,r;
scanf("%lf%lf",&n,&r);
double ang=2.0*PI/(2.0*n);
double D=1.0/sin(ang);
double ans=r/(D-1.0);
printf("%.10f",ans);
return 0;
}
D. Dasha and Chess
题意
给你999*999的棋盘,上面有666个士兵和一个国王,你来操纵国王,checker来操纵士兵,最初国王所在行和列没有任何士兵,每次你可以向你八连通方向任意一个格子移动,而士兵可以从一个地方到任何一个没有棋子的地方,你获胜的条件是你挪动之后,你所在的行或列有一个或多个士兵,交互题,提供2000次操作之后必胜的方法。
做法
这道题一定要仔细思考数据范围,首先我们将国王挪到中间,之后我们所面临的最差情况是这样的。
666个棋子被均分在四个角落,这时我们发现,如果我们向着一个角移动,那么对方要改变相应的三个角的棋子的位置,而最差情况下三个角的最大和是166+167+167=500.而我们只需要499步就可以挪动到边界,也就是说,我们只要向着三个方向加起来最大的那个角移动,必胜!
这道题实现起来有很多细节,在斜向移动的过程中要判断要移的方向上是否已经有棋子,如果已经有棋子,我们只需要挪动x或者y即可获得胜利。
代码
#include<stdio.h>
#include<stdlib.h>
const int maxn = 1005;
int x,y,u,v,w,px[maxn],py[maxn],vis[maxn][maxn],sum[5],sum2[5];
int dis[5][2]={0,0,-1,-1,-1,1,1,-1,1,1};
void move_(int xx,int yy)
{
x+=xx;
y+=yy;
if(vis[x][y]) x-=xx;
printf("%d %d\n",x,y);
fflush(stdout);
scanf("%d%d%d",&u,&v,&w);
if(u==-1&&v==-1&&w==-1) exit(0);
vis[px[u]][py[u]]=0;
vis[v][w]=1;
px[u]=v;
py[u]=w;
return ;
}
int main()
{
scanf("%d%d",&x,&y);
for(int i=1;i<=666;i++)
{
scanf("%d%d",&px[i],&py[i]);
vis[px[i]][py[i]]=1;
}
int dir=-1;
while(x<500) move_(1,0);
while(y<500) move_(0,1);
while(x>500) move_(-1,0);
while(y>500) move_(0,-1);
for(int i=1;i<=499;i++) for(int j=1;j<=499;j++) if(vis[i][j]) sum[1]++;
for(int i=1;i<=499;i++) for(int j=501;j<=999;j++) if(vis[i][j]) sum[2]++;
for(int i=501;i<=999;i++) for(int j=1;j<=499;j++) if(vis[i][j]) sum[3]++;
for(int i=501;i<=999;i++) for(int j=501;j<=999;j++) if(vis[i][j]) sum[4]++;
sum2[1]=sum[1]+sum[2]+sum[3];
sum2[2]=sum[2]+sum[1]+sum[4];
sum2[3]=sum[3]+sum[1]+sum[4];
sum2[4]=sum[4]+sum[2]+sum[3];
int maxx=0;
for(int i=1;i<=4;i++)
{
if(sum2[i]>maxx)
{
maxx=sum2[i];
dir=i;
}
}
while(true) move_(dis[dir][0],dis[dir][1]);
return 0;
}
E. Andrew and Taxi
题意
给你一个有边权的有向图,反转一条边的代价是这条边的边权,反转多个边的代价是所有反转边里面边权最大的那条边的边权,问让这个图不存在环的最小代价,以及被反转的边的编号。
做法
首先求最小代价,我们只需要二分这个代价,check就是看删除所有小于这个代价的边之后是否有环。
输出边集,要用到拓扑排序的一个性质
一个图进行拓扑排序后,对于每一个有向边u->v都存在u的拓扑序<v的拓扑序,则这个图上无环
之后我们就枚举所有小于当前代价的边,对所有不满足条件的边反转就可以。
这样就保证反转之后无环,而且代价最小。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
typedef pair <int, int> pii;
const int maxn = 1e5+5;
#define Se second
#define Fi first
int n,m;
vector<pii> G[maxn];
int top[maxn];
int ind[maxn];
int que[maxn];
bool check(int mid)
{
int qt=0;
for(int i=1;i<=n;i++) ind[i]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<G[i].size();j++)
{
if(G[i][j].Se>mid) ind[G[i][j].Fi]++;
}
}
for(int i=1;i<=n;i++)
{
if(ind[i]==0)
{
que[qt++]=i;
top[i]=qt;
}
}
for(int i=0;i<qt;i++)
{
int u=que[i];
for(int j=0;j<G[u].size();j++)
{
if(G[u][j].Se<=mid) continue;
if(--ind[G[u][j].Fi]==0)
{
que[qt++]=G[u][j].Fi;
top[G[u][j].Fi]=qt;
}
}
}
for(int i=1;i<=n;i++) if(ind[i]) return false;
return true;
}
struct Edge
{
int u,v,w;
Edge(){}
Edge(int uu,int vv,int ww)
{
u=uu;
v=vv;
w=ww;
}
}E[maxn];
vector<int> ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(pii(v,w));
E[i]=Edge(u,v,w);
}
int l=0,r=1000000000,mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid)) r=mid-1;
else l=mid+1;
}
check(l);
for(int i=1;i<=m;i++)
{
int fi=E[i].u;
int to=E[i].v;
if(E[i].w<=l&&top[fi]>top[to]) ans.push_back(i);
}
printf("%d %d\n",l,ans.size());
for(int i=0;i<ans.size();i++)
{
printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
}
return 0;
}
F. Ivan and Burgers
题意
n个数,q次询问,每次询问一个区间内选出任意个数的异或最大值。
1
<
=
n
<
=
5
∗
1
0
5
1<=n<=5*10^5
1<=n<=5∗105
1
<
=
q
<
=
5
∗
1
0
5
1<=q<=5*10^5
1<=q<=5∗105
做法
考虑离线的做法,首先将询问按照右端点排序,之后对于每个询问,将[1,R]范围内的数插入线性基,对于每个基保留最右面的并记录位置,由于线性基中只有log个元素,我们可以遍历一遍所有元素,取出在L之后的基构造最大值,便是答案。总复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 5e5+5;
int a[maxn],pos[maxn];
bool insert_(int val,int p)
{
for (int i=20;i>=0;i--)
{
if (val&(1LL<<i))
{
if (!a[i])
{
a[i]=val;
pos[i]=p;
break;
}
if(pos[i]<p)
{
swap(pos[i],p);
swap(a[i],val);
}
val^=a[i];
}
}
return val>0;
}
int query_max(int l)
{
int ret=0;
for (int i=20;i>=0;i--)
if ((ret^a[i])>ret&&pos[i]>=l)
ret^=a[i];
return ret;
}
int c[maxn];
struct data
{
int l,r,id;
}Q[maxn];
bool cmp(const data &a,const data &b)
{
if(a.r==b.r) return a.l<b.l;
return a.r<b.r;
}
int ans[maxn];
int main()
{
int n,q;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+1+q,cmp);
int r=1;
for(int i=1;i<=q;i++)
{
while(r<=Q[i].r)
{
insert_(c[r],r);
r++;
}
ans[Q[i].id]=query_max(Q[i].l);
}
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}