Full_of_Boys训练1总结

题目来源: 2017-2018 ACM-ICPC Northern Eurasia (Northeastern European Regional) Contest (NEERC 17)

A. Archery Tournament

每次查询,找这个位置前面的15个圆,后边15个圆来更新答案。set维护一下圆就行。为什么对,官方题解:可以证明经过某一条竖线的圆不超过logC个(C=1e9)

#include <bits/stdc++.h>
#define pb(x) push_back(x)
const int maxn = 200000+5;
const int lim = 15;
typedef long long ll;
using namespace std;
int n;
struct qq{int opt;ll x,y;}Q[maxn];
struct node{
    int id;ll x,y;
    node(){}
    node(ll a,ll b,int c){x=a;y=b;id=c;}
    bool operator < (const node a)const{
        if(a.x==x)return y < a.y;
        return x < a.x;
    }
};
set<node>s;
set<node>::iterator it,it1;
ll dis(ll x1,ll y1,ll x2,ll y2){
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
ll in_cir(ll x1,ll y1,ll x2,ll y2){
    if(dis(x1,y1,x2,y2)<y1*y1)return 1;
    return 0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i){int opt;ll x,y;
        scanf("%d%I64d%I64d",&opt,&x,&y);
        Q[i].opt=opt;Q[i].x=x;Q[i].y=y;
    }
    for(int i=1;i<=n;++i){
        if(Q[i].opt==1)s.insert(node(Q[i].x,Q[i].y,i));
        else{
            int cc=0,ans=-1;
            it = s.lower_bound(node(Q[i].x,0,0));
            for(;it!=s.end()&&cc<=lim;++cc,++it){
                if(in_cir((*it).x,(*it).y,Q[i].x,Q[i].y)){
                    s.erase(it);ans=(*it).id;break;
                }
            }
            cc=0;
            it1=s.lower_bound(node(Q[i].x,0,0));
            for(;it1!=s.begin()&&cc<=lim;--it1,++cc){
                if(in_cir((*it1).x,(*it1).y,Q[i].x,Q[i].y)){
                    s.erase(it1);ans=(*it1).id;break;
                }
            }
            it1=s.begin();
            if(in_cir((*it1).x,(*it1).y,Q[i].x,Q[i].y)){
                s.erase(it1);ans=(*it1).id;
            }
            printf("%d
",ans);
        }
    }
    return 0;
}

反思:这种重圆不多,现场猜测到了。。。xi ka xi。。。我的思路是直接将,每个圆包含的关键点暴力的加上,这个圆的标记,会Mle,但应该不会Tle,队友说线段树,当场只想到一种比暴力还糟的做法。。。下来思考了一下,既然直接暴力mle,那我分块暴力。每个块维护一个set存圆,然后添加和删除圆,就直接更新这个圆覆盖过的块。对于查询,每次暴力这个块和他左右两边的块。??后来觉得不对,毕竟按横坐标分块暴力,直觉上无法保证复杂度。那可不可以在块内二分出那个横坐标最近的圆?然后小范围暴力一下?那还分啥块?一个set就搞定了?后来,看了下题解发现小范围暴力,是有正确性的。艰难AC。。。另一种思路,我们分块到底?既然不能按横坐标分块,那就按照圆分块?这样每个圆内最多有3*√n个x坐标?复杂度还很稳定。思考一下,发现如果不用这个结论,还是没有特别真的做法。。。还是直接set做吧。223

现场时,脑子就跟挂机了一样,下来分析一下,感觉并不是一道不可做的题。这是辣鸡RRRR_wys长期的问题。。。希望有高人,能指点一下,如何保证现场赛脑子在线。。。

B. Box

分类讨论

#include <bits/stdc++.h>
using namespace std;
int a[3],w,h,ww,hh;
bool ck1(){
    ww=a[1]*2+a[2]*2;
    hh=a[0]+a[2]*2;
    if(ww<=w&&hh<=h)return 1;

    ww=a[1]*2+a[2]*2;
    hh=a[0]+a[1]+a[2];
    if(ww<=w&&hh<=h)return 1;

    ww=a[0]+a[1]+a[2]*3;
    hh=a[0]+a[1];
    if(ww<=w&&hh<=h)return 1;

    ww=a[0]+a[1]+a[2]*2;
    hh=a[0]+a[1]+a[2];
    if(ww<=w&&hh<=h)return 1;

    ww=a[0]+a[1]*2+a[2];
    hh=a[0]+a[2]*2;
    if(ww<=w&&hh<=h)return 1;

    ww=a[0]*2+a[1]+a[2];
    hh=a[0]+a[1]+a[2];
    if(ww<=w&&hh<=h)return 1;

    ww=a[0]*2+a[1];
    hh=a[0]+a[1]+a[2]*2;
    if(ww<=w&&hh<=h)return 1;
    return 0;
}
bool ck(){
    sort(a,a+3);
    for(int ti=0;ti<6;++ti){
        if(ck1())return 1;
        next_permutation(a,a+3);
    }
    return 0;
}
int main()
{
    scanf("%d%d%d%d%d",&a[0],&a[1],&a[2],&w,&h);
    if(ck()){
        puts("Yes");return 0;
    }
    swap(w,h);
    if(ck()){
        puts("Yes");return 0;
    }
    puts("No");
    return 0;
}

C. Connections

题意可以理解为:求一些有向边使得整张图是一个强连通图。删除这些分量中的边就是答案。根据一种贪心的构造,最多加入2(n-1)条边,一定可以使得整张图强连通。相当于一颗树,它的每一条边都是双向边。那么就可以通过Kosaraju算法,处理出在强联通分量中的边做了。

#include <bits/stdc++.h>
#define pb(x) push_back(x)
typedef long long ll;
const int maxn = 300000+7;
using namespace std;
int n,m;
struct edge{int u,v,nxt;}E[maxn][2];
int h[maxn][2],cc;
void add(int u,int v){++cc;
    E[cc][0].u=u;E[cc][0].v=v;E[cc][0].nxt=h[u][0];h[u][0]=cc;
    E[cc][1].u=v;E[cc][1].v=u;E[cc][1].nxt=h[v][1];h[v][1]=cc;
}
bool vis[maxn],used[maxn];int stc[maxn],tp;
void dfs1(int u){
    vis[u]=1;
    for(int i=h[u][0];~i;i=E[i][0].nxt){
        int v=E[i][0].v;
        if(!vis[v]){
            used[i]=1;
            dfs1(v);
        }
    }
    stc[++tp]=u;
}
void dfs2(int u){
    vis[u]=0;
    for(int i=h[u][1];~i;i=E[i][1].nxt){
        int v=E[i][1].v;
        if(vis[v]){
            used[i]=1;
            dfs2(v);
        }
    }
}
int T;
int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        cc=tp=0;
        memset(h,-1,sizeof(h));
        memset(used,0,sizeof(used));
        for(int i=1;i<=m;++i){int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        for(int i=1;i<=n;++i)if(!vis[i])dfs1(i);
        for(int i=n;i>=1;--i)if(vis[stc[i]])dfs2(stc[i]);
        int tn = 2*n;
        for(int i=1;i<=m;++i)if(used[i])--tn;
        for(int i=1;i<=m;++i)if(!used[i]&&tn>0)--tn,used[i]=1;
        for(int i=1;i<=m;++i)if(!used[i]){
            printf("%d %d
",E[i][0].u,E[i][0].v);
        }
    }
    return 0;
}

反思:做这道题的时候,看到榜上ac人数较多,就猜测是个计算度数,然后胡乱贪心,wa掉之后,依然不死心,导致gg。zyc赛中提到一种,一边判环缩环,记录边数的做法(有问题)。并没有被我重视。。。仔细考虑一下,就会发现这个思路与强联通相似之处,Kosaraju算法通过在第一次dfs的标记的顺序,进行第二次dfs,使得我们不会指向多余的边(感觉上......不会证明)。胡思乱想坑队友。。。

 D. Designing the Toy

E. Easy Quest

F. The Final Level

G. The Great Wall

H. Hack

I. Interactive Sort

J. Journey from Petersburg to Moscow

K. Knapsack Cryptosystem

L. Laminar Family

把每条路径按照长度降序排列,每次随机一个值把他异或到整个路径上,然后查询这个路径是否所有数字都相同即可。实现方法,先树剖,用线段树区间间异或,区间询问是否所有数全部相同,为了完成合并,还要维护区间左右端点的值。可是,wa在test26 。。。各位聚聚,能不能给施舍弱几组数据。。。wa的:https://paste.ubuntu.com/p/dy4sBzXrVZ/

-------------------------------------------------------------------------------------------2018/04/27 更新

之后,想了一下,为什么要用区间异或,这种东西呢?排序之后,直接区间赋值,存下数字不同的两个位置,当给定区间包含某一对这样的位置时,区间数字就不相同。这个直接开个set在里面二分第一个大于左端点的左点,check一下就行了。嘴完了。。。

感觉不对。。。凉了。

原文地址:https://www.cnblogs.com/RRRR-wys/p/8879075.html