【Codeforces Round #532 (Div. 2) 】A.B.C.D.E.F

前言

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 &lt; = k &lt; n &lt; = 100 2&lt;=k&lt;n&lt;=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 &lt; = n , m &lt; = 1 0 5 1&lt;=n,m&lt;=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

题意

给你一个如图所示的多圆绕一圆,问小圆半径
Codeforces-532-C
做法

对于任意一个小圆,有下面这张图,所以直接用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 &lt; = n &lt; = 5 ∗ 1 0 5 1&lt;=n&lt;=5*10^5 1<=n<=5105
1 &lt; = q &lt; = 5 ∗ 1 0 5 1&lt;=q&lt;=5*10^5 1<=q<=5105
做法

考虑离线的做法,首先将询问按照右端点排序,之后对于每个询问,将[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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值