zoukankan      html  css  js  c++  java
  • 【做题】提高组过关测试题1

    CF980E. The Number Games

    题意:有一棵含有(n)个结点的树。求所有含有(n-k)个结点的联通块中,结点编号从大到小排序,字典序最大的联通块。
    (n leq 10^6)

    显然可以贪心。按编号从大到小枚举结点,能加入联通块的就一定加入联通块。我们以(n)号点为根结点,每次就是把一个结点到根的路径上所有点加入联通块。当然,其中有一些结点已经加入联通块了。
    考虑到每个结点最多被加入联通块1次,我们可以在加入时暴力。至于判断一个结点是否可以加入,用倍增查找它到根的路径上深度最大的已经被加入联通块的结点,我们就能得出要加入这个点,同时还要加入多少个结点。
    时间复杂度(O(n log n))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1000010, MP = 22;
    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 vis[N],fa[N],anc[N][MP],dep[N],n,k,cur;
    void dfs(int pos) {
      anc[pos][0] = fa[pos];
      dep[pos] = dep[fa[pos]] + 1;
      for (int i = 1 ; i < MP ; ++ i) {
        anc[pos][i] = anc[anc[pos][i-1]][i-1];
        if (!anc[pos][i]) break;
      }
      for (int i = fir[pos] ; i ; i = con[i].la) {
        if (con[i].b == fa[pos]) continue;
        fa[con[i].b] = pos;
        dfs(con[i].b);
      }
    }
    int query(int x) {
      int t = x;
      for (int i = MP - 1 ; i >= 0 ; -- i)
        if (!vis[anc[x][i]])
          x = anc[x][i];
      x = fa[x];
      return dep[t] - dep[x];
    }
    int main() {
      int a,b;
      scanf("%d%d",&n,&k);
      for (int i = 1 ; i < n ; ++ i) {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
      }
      dfs(n);
      vis[0] = 1;
      vis[n] = cur = 1;
      k = n - k;
      for (int i = n - 1 ; i >= 1 ; -- i) if (!vis[i]) {
        if (cur + query(i) <= k) {
          int pos = i;
          while (!vis[pos]) {
    	vis[pos] = 1;
    	++ cur;
    	pos = fa[pos];
          }
        }
      }
      for (int i = 1 ; i <= n ; ++ i)
        if (!vis[i]) printf("%d ",i);
      puts("");
      return 0;
    }
    

    CF512B. Fox And Jumping

    题意:有(n)个卡片,每个卡片都有一个权值(l_i)与费用(c_i)。你需要从其中购买若干张卡片,使得它们权值的gcd为1。求最小的费用。若无解,输出-1。
    (n leq 300, \, l_i leq 10^9)

    问题就在于在(l_i)范围内的质数太多了。
    然后有一个巧妙的小技巧,因为一个数的互不相同的质因子个数是很少的,所以我们枚举选取的第一张卡片。我们dp记录状态时只要考虑它所包含的质数就行了。
    时间复杂度(O(n^2 imes 2^w)),其中(w)为最多含有的互不相同的质因子个数,且(w leq 10)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 310, M = 1 << 10, MAX = 100000, INF = 0x3f3f3f3f;
    int isp[MAX+10],pri[MAX],pcnt,n,l[N],c[N],dp[M],ans=INF;
    vector<int> fac[N];
    void prework() {
      for (int i = 2 ; i <= MAX ; ++ i) {
        if (!isp[i]) pri[++pcnt] = i;
        for (int j = 1 ; j <= pcnt && pri[j] * i <= MAX ; ++ j) {
          isp[pri[j] * i] = 1;
          if (i % pri[j] == 0) break;
        }
      }
    }
    int main() {
      scanf("%d",&n);
      for (int i = 1 ; i <= n ; ++ i)
        scanf("%d",&l[i]);
      for (int i = 1 ; i <= n ; ++ i)
        scanf("%d",&c[i]);
      prework();
      for (int i = 1 ; i <= n ; ++ i) {
        int tmp = l[i];
        for (int j = 1 ; j <= pcnt ; ++ j)
          if (tmp % pri[j] == 0) {
    	fac[i].push_back(pri[j]);
    	while (tmp % pri[j] == 0) tmp /= pri[j];
          }
        if (tmp != 1) fac[i].push_back(tmp);
      }
      先手 i = 1 ; i <= n ; ++ i) {
        int tot = (1 << fac[i].size());
        memset(dp,0x3f,sizeof dp);
        dp[tot-1] = c[i];
        for (int j = i + 1 ; j <= n ; ++ j) {
          int tmp = 0;
          for (int k = 0 ; k < (int)fac[i].size() ; ++ k)
    	if (l[j] % fac[i][k] == 0) tmp |= (1 << k);
          for (int k = 0 ; k < tot ; ++ k)
    	dp[k & tmp] = min(dp[k & tmp],dp[k] + c[j]);
        }
        ans = min(ans,dp[0]);
      }
      if (ans == INF) puts("-1");
      else printf("%d
    ",ans);
      return 0;
    }
    

    CF455B. A Lot of Games

    题意:A和B在玩一个博弈游戏。他们有若干个个长度总和不大于(l)的字符串。在它们建成的trie树上,从根结点开始,两个人轮流移动同一个棋子,不断向它的儿子结点走。先走到叶结点的人输。他们连续玩(k)轮游戏,第(i-1)局输的人在第(i)局先手。他们的目的都是在第(k)局取胜。问都采取最有决策下,谁能取胜。
    (l leq 10^5, \, k <= 10^9)

    关键在于一个细节,为了在第(k)局取胜,可以故意在某一局输掉以换取先手权。因此,我们在树上dp时,要记录在每个结点先手的可能情况:

    • 只能赢。
    • 只能输。
    • 由自己决定赢或输。
    • 由对手决定赢或输。

    具体细节请自行讨论。
    时间复杂度(O(l))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    int ch[N][26], dp[N], n, k, l, cnt = 1, sz[N];
    char tmp[N];
    void dfs(int pos) {
      int key = 0;
      dp[pos] = -1;
      for (int i = 0 ; i < 26 ; ++ i) if (ch[pos][i]) {
        dfs(ch[pos][i]);
        key = 1;
        switch (dp[ch[pos][i]]) {
        case 1:if (dp[pos] == -1) dp[pos] = 2;
          if (dp[pos] == 1) dp[pos] = 3; break;
        case 2:if (dp[pos] == -1) dp[pos] = 1;
          if (dp[pos] == 2) dp[pos] = 3; break;
        case -1: dp[pos] = 3;
        }
      }
      if (key == 0)
        dp[pos] = 1;
      if (pos == 1 && dp[pos] == 1) dp[pos] = 2;
      else if (pos == 1 && dp[pos] == 2) dp[pos] = 1;
    }
    int main() {
      scanf("%d%d",&n,&k);
      for (int i = 1 ; i <= n ; ++ i) {
        scanf("%s",tmp+1);
        l = strlen(tmp+1);
        for (int j = 1, p = 1 ; j <= l ; ++ j) {
          if (!ch[p][tmp[j] - 'a'])
    	ch[p][tmp[j] - 'a'] = ++cnt;
          p = ch[p][tmp[j] - 'a'];
        }
      }
      dfs(1);
      k = k&1;
      if (dp[1] == 3) puts("First");
      else if (dp[1] == 2) puts("Second");
      else if (dp[1] == 1) {
        if (k) puts("First");
        else puts("Second");
      } else puts("Second");
      return 0;
    }
    

    小结:其实题目都不难,但却时常被卡住。这说明自己思维还不够灵活。
  • 相关阅读:
    vue—子调父 $emit (把子组件的数据传给父组件)
    解决 Error: EBUSY: resource busy or locked, rmdir 'E:/...'问题
    php中session原理及安全性问题
    MySQL函数大全及用法示例
    php基础语法
    常用sql语句
    php表单传值--GET和POST
    jQuery插件的使用方法
    $.ajax()方法详解
    php文件上传
  • 原文地址:https://www.cnblogs.com/cly-none/p/9274208.html
Copyright © 2011-2022 走看看