• 【做题】neerc2017的A、C、I、L


    A - Archery Tournament

    一开始往化简公式的方向去想,结果没什么用。
    考虑与一条垂线相交的圆的个数。不难YY,当圆的个数最多时,大概就是这个样子的:
    与垂线相交的圆
    我们稍微推一下式子,然后就能发现其个数不超过(O(log C)),其中(C)为坐标范围(即1e9)。
    接下来的事情就简单了,我们把数据离散化后就能用线段树查询过某一条垂线的所有圆了,再暴力判断是哪一个就可以了。
    时间复杂度(O(n log ^2 n))

    #include <bits/stdc++.h>
    #define sqr(x) (1ll * (x) * (x))
    using namespace std;
    const int N = 400010;
    struct circle {
      int x,y;
      bool inside(int a,int b) {
        return sqr(x - a) + sqr(b - y) < sqr(y);
      }
    } cir[N];
    int n;
    struct data {
      int tp,x,y;
    } dat[N];
    vector<int> tmp,ins;
    set<int> q[N << 2];
    void modify(int l,int r,int v,int sgn,int x=1,int lp=1,int rp = n) {
      if (l > rp || r < lp) return;
      if (lp >= l && rp <= r) {
        if (sgn) q[x].insert(v);
        else q[x].erase(v);
        return;
      }
      int mid = (lp + rp) >> 1;
      modify(l,r,v,sgn,x<<1,lp,mid);
      modify(l,r,v,sgn,x<<1|1,mid+1,rp);
    }
    void add(int id,int sgn) {
      int l = lower_bound(tmp.begin(),tmp.end(),dat[id].x - dat[id].y) - tmp.begin() + 1;
      int r = upper_bound(tmp.begin(),tmp.end(),dat[id].x + dat[id].y) - tmp.begin();
      modify(l,r,id,sgn);
    }
    void query(int p,int x=1,int lp=1,int rp=n) {
      if (lp > p || rp < p) return;
      for (set<int>::iterator t = q[x].begin() ; t != q[x].end() ; ++ t)
        ins.push_back(*t);
      if (lp == rp) return; 
      int mid = (lp + rp) >> 1;
      if (p <= mid) query(p,x<<1,lp,mid);
      else query(p,x<<1|1,mid+1,rp);
    }
    int doit(int a,int b) {
      ins.clear();
      int p = lower_bound(tmp.begin(),tmp.end(),a) - tmp.begin() + 1;
      query(p);
      for (int i = 0 ; i < (int)ins.size() ; ++ i) {
        if (cir[ins[i]].inside(a,b)) {
          add(ins[i],0);
          return ins[i];
        }
      }
      return -1;
    }
    int main() {
      scanf("%d",&n);
      for (int i = 1 ; i <= n ; ++ i) {
        scanf("%d%d%d",&dat[i].tp,&dat[i].x,&dat[i].y);
        if (dat[i].tp == 2)
          tmp.push_back(dat[i].x);
        else cir[i].x = dat[i].x, cir[i].y = dat[i].y;
      }
      sort(tmp.begin(),tmp.end());
      tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
      for (int i = 1 ; i <= n ; ++ i) {
        if (dat[i].tp == 1)
          add(i,1);
        else printf("%d
    ",doit(dat[i].x,dat[i].y));
      }
      return 0;
    }
    

    C - Connections

    类比targan,我们考虑构造一棵dfs树。那么,单靠dfs树上的这些边,我们就能保证从根结点能到达任意一个结点。然后就只用让任意一个结点都能到达根结点就好了。于是我们在反图上以同样的点为根结点建dfs树(反图仍然强连通),把这两棵树并在一起,边数最多为(2n-2),能满足本题要求。
    时间复杂度(O(n))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    int n,m,a[N],b[N],ned[N],vis[N]; 
    struct edge {
      int la,b,id;
    };
    struct tree {
      edge con[N << 1];
      int tot,fir[N];
      void add(int from,int to,int num) {
        con[++tot] = (edge) {fir[from],to,num};
        fir[from] = tot;
      }
      void init() {
        tot = 0;
        for (int i = 1 ; i <= n ; ++ i)
          fir[i] = 0;
      }
      void dfs(int pos) {
        vis[pos] = 1;
        for (int i = fir[pos] ; i ; i = con[i].la) {
          if (vis[con[i].b]) continue;
          ned[con[i].id] = 1;
          dfs(con[i].b);
        }
      }
    } g,ig;
    int main() {
      int T;
      scanf("%d",&T);
      while (T --) {
        scanf("%d%d",&n,&m);
        g.init();
        ig.init();
        for (int i = 1 ; i <= m ; ++ i)
          scanf("%d%d",&a[i],&b[i]), g.add(a[i],b[i],i), ig.add(b[i],a[i],i), ned[i] = 0;
        for (int i = 1 ; i <= n ; ++ i)
          vis[i] = 0; 
        g.dfs(1);
        for (int i = 1 ; i <= n ; ++ i)
          vis[i] = 0; 
        ig.dfs(1);
        for (int i = 1, j = 1 ; i <= m && j <= m - 2 * n ; ++ i)
          if (!ned[i]) printf("%d %d
    ",a[i],b[i]), ++ j; 
      }
      return 0;
    } 
    

    I - Interactive Sort

    首先一个重要的性质:数据随机
    考虑通过偶数比较来把奇数排序。假如我们把任意一个偶数与所有奇数比较,那么我们就能够把奇数分成两组,并且得出这个偶数是什么。把一些数随机地分为两组,这与快速排序十分相似。因此,我们希望能避免把每个偶数都与所有奇数比较。
    假设我们现在已经把奇数分为(k)组,那么,对于我们新取出的一个偶数(x),其中至少有(k-1)组满足其中所有元素都大于(或小于)(x)。然而,具体地确定被(x)划分的是哪一组是很困难的,因为那一组有的元素大于(x),有的小于。那我们不妨就只确定到两组,这个可以用二分实现。然后我们就把两组划分为三组。
    这个算法本质就是快速排序,只不过多了二分查找。
    然后粗糙计算一下询问次数的常数。把两组划分为三组相较于把一组划分为两组大概是2倍常数,二分查找估计为常数加1。快排期望复杂度的常数是2,故这个算法的常数可以认为是5。对于(n=5000),常数为5的(O(n log n))次询问,不会超出本题的限制。
    时间复杂度上,暴力维护分组是(O(n^2))的,可以用数据结果做到(O(n log^2 n))

    #include <bits/stdc++.h>
    #define gc() getchar()
    using namespace std;
    const int N = 10010;
    struct data {
      int l,r;
    } dat[N]; 
    int n,cnt,a[N],tmp[N],tcnt,ans[N];
    int ask(int x,int y) {
      char tmp;
      printf("? %d %d
    ",x,y);
      fflush(stdout);
      for (tmp = gc() ; tmp != '<' && tmp != '>' ; tmp = gc()); 
      return tmp == '<'; 
    }
    int main() {
      int l,r,mid,p,num;
      scanf("%d",&n);
      for (int i = 1 ; i <= (n+1)/2 ; ++ i) a[i] = i; 
      dat[1] = (data) {1,(n+1)/2};
      cnt = 1;
      for (int i = 1 ; i <= n/2 ; ++ i) {
        l = 1, r = cnt;
        while (l + 1 < r) {
          mid = (l + r) >> 1;
          if (ask(i,a[dat[mid].l])) r = mid;
          else l = mid;
        }
        p = dat[l].l;
        num = tcnt = 0;
        for (int j = dat[l].l ; j <= dat[l].r ; ++ j)
          if (ask(i,a[j])) tmp[++tcnt] = a[j];
          else a[p++] = a[j], num ++;
        for (int j = 1 ; j <= tcnt ; ++ j)
          a[p+j-1] = tmp[j];
        if (num != 0 && num != dat[l].r - dat[l].l + 1) {
          ans[i] = (num + dat[l].l-1) << 1;
          ++ cnt;
          for (int j = cnt ; j >= l + 2 ; -- j)
    	      dat[j] = dat[j-1];
          dat[l+1] = (data) {dat[l].l + num,dat[l].r};
          dat[l] = (data) {dat[l].l,dat[l].l + num - 1};
          continue;
        }
        if (l == r) {
          ans[i] = n;
          continue;
        }
        p = dat[r].l;
        num = tcnt = 0;
        for (int j = dat[r].l ; j <= dat[r].r ; ++ j)
          if (ask(i,a[j])) tmp[++tcnt] = a[j];
          else a[p++] = a[j], num ++;
        for (int j = 1 ; j <= tcnt ; ++ j)
          a[p+j-1] = tmp[j];
        ans[i] = (num + dat[r].l - 1) << 1;
        if (num == 0 || num == dat[r].r - dat[r].l + 1) continue;
        ++ cnt;
        for (int j = cnt ; j >= r + 2 ; -- j)
          dat[j] = dat[j-1];
        dat[r+1] = (data) {dat[r].l + num,dat[r].r};
        dat[r] = (data) {dat[r].l,dat[r].l + num - 1}; 
      }
      printf("!");
      for (int i = 1 ; i <= n/2 ; ++ i)
        printf(" %d",ans[i]);
      for (int i = 1 ; i <= (n+1)/2 ; ++ i)
        ans[a[i]] = (i<<1) - 1;
      for (int i = 1 ; i <= (n+1)/2 ; ++ i)
        printf(" %d",ans[i]);
      puts("");
      return 0;
    }
    

    L - Laminar Family

    这题有多个做法,这里仅涉及我本人的做法。
    我们考虑按长度从大到小枚举路径,那么当我们枚举到路径(k)时,我们就不需要考虑(k)包含其他路径的情况,因为能被(k)包含的路径还没加进来。那么,(k)这一条路径要么已经完全被其他另一条路径覆盖了,要么完全没被覆盖。于是,我们枚举路径时对这条路径上的所有结点都染色(不同路径的颜色不同),那么答案是Yes就等价于,对于所有路径,它上面的结点在染上它的颜色之前的颜色都是相同的(或都没有颜色)。这个可以用树链剖分+线段树实现。
    时间复杂度(O(n log ^2 n))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    struct edge {
      int la,b;
    } con[N << 1];
    int tot,fir[N];
    void add(int from,int to) {
      con[++tot] = (edge) {fir[from],to};
      fir[from] = tot;
    }
    int fa[N],sz[N],hson[N],top[N],dfn[N],dep[N],cnt,n,m,ans;
    void dfs_init(int pos) {
      dep[pos] = dep[fa[pos]] + 1;
      sz[pos] = 1;
      for (int i = fir[pos] ; i ; i = con[i].la) {
        if (con[i].b == fa[pos]) continue;
        fa[con[i].b] = pos;
        dfs_init(con[i].b);
        sz[pos] += sz[con[i].b];
        if (sz[con[i].b] > sz[hson[pos]])
          hson[pos] = con[i].b;
      }
    }
    void dfs_build(int pos,int tp) {
      top[pos] = tp;
      dfn[pos] = ++cnt;
      if (hson[pos]) dfs_build(hson[pos],tp);
      for (int i = fir[pos] ; i ; i = con[i].la) {
        if (con[i].b == fa[pos] || con[i].b == hson[pos])
          continue;
        dfs_build(con[i].b,con[i].b);
      }
    }
    int lca(int x,int y) {
      while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x,y);
        x = fa[top[x]];
      }
      if (dep[x] > dep[y]) swap(x,y);
      return x;
    }
    struct node {
      int tag,val;
    } t[N << 2];
    inline void puttag(int x,int v) {
      t[x].tag = v;
      t[x].val = v;
    }
    void push_down(int x) {
      if (!t[x].tag) return;
      puttag(x<<1,t[x].tag);
      puttag(x<<1|1,t[x].tag);
      t[x].tag = 0;
    }
    inline int merge(int x,int y) {
      if (x == -2) return y;
      if (y == -2) return x;
      if (x == -1 || y == -1) return -1;
      if (x != y) return -1;
      return x;
    }
    void push_up(int x) {
      if (t[x].tag) push_down(x);
      t[x].val = merge(t[x<<1].val,t[x<<1|1].val);
    }
    void modify(int l,int r,int v,int x=1,int lp=1,int rp=n) {
      if (l > rp || r < lp) return;
      if (lp >= l && rp <= r)
        return (void)(puttag(x,v));
      int mid = (lp + rp) >> 1;
      push_down(x);
      modify(l,r,v,x<<1,lp,mid);
      modify(l,r,v,x<<1|1,mid+1,rp);
      push_up(x);
    }
    int query(int l,int r,int x=1,int lp=1,int rp=n) {
      if (l > rp || r < lp) return -2;
      if (lp >= l && rp <= r)
        return t[x].val;
      int mid = (lp + rp) >> 1;
      push_down(x);
      return merge(query(l,r,x<<1,lp,mid),query(l,r,x<<1|1,mid+1,rp));
    }
    bool doit(int x,int y,int id) {
      int res = 1, tmp = -2;
      while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x,y);
        tmp = merge(tmp,query(dfn[top[x]],dfn[x]));
        if (tmp == -1) res = 0;
        modify(dfn[top[x]],dfn[x],id);
        x = fa[top[x]];
      }
      if (dep[x] < dep[y]) swap(x,y);
      tmp = merge(tmp,query(dfn[y],dfn[x]));
      if (tmp == -1) res = 0;
      modify(dfn[y],dfn[x],id);
      return res;
    }
    struct data {
      int u,v,len;
      bool operator < (const data& x) const {
        return len > x.len;
      }
    } dat[N];
    int main() {
      int a,b,c;
      scanf("%d%d",&n,&m);
      for (int i = 1 ; i < n ; ++ i) {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
      }
      dfs_init(1);
      dfs_build(1,1);
      for (int i = 1 ; i <= m ; ++ i) {
        scanf("%d%d",&a,&b);
        c = lca(a,b);
        dat[i] = (data) {a,b,dep[a] + dep[b] - 2 * dep[c]};
      }
      sort(dat+1,dat+m+1);
      ans = 1;
      for (int i = 1 ; i <= m ; ++ i)
        ans &= doit(dat[i].u,dat[i].v,i);
      if (ans) puts("Yes");
      else puts("No");
      return 0;
    }
    

    小结:还有好几道题根本没法补……感觉自己缺少创造性的思维,也许是应该多做这种类型的题目了。

  • 相关阅读:
    leetcode-String to Integer (atoi)
    2014薪水
    Ubunt下的软件集
    ubuntu常用软件
    python模块安装
    ubuntu下玩三国杀
    递归函数
    匿名函数
    装饰器函数
    生成器
  • 原文地址:https://www.cnblogs.com/cly-none/p/9032262.html
Copyright © 2020-2023  润新知