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

    CF264C. Choosing Balls

    题意:你有(n)个球,每个都有颜色和权值(c_i)(w_i)。定义它的子序列的权值为:对于其中的每一个球,如果它在子序列中的上一个球(必须存在)与它同颜色,则贡献(a imes w_i)的权值。否则,贡献(b imes w_i)的权值。其中,(a)(b)都是常量。

    (q)次询问,每次给出(a)(b),问所有子序列中最大的权值是多少。

    (n leq 10^5, \, q leq 500)

    dp是显然的。我们记dp[c]为目前以颜色c结尾的子序列中,最大的权值。那么,我们枚举每一个球。设当前枚举到第(i)个球,就能得到

    • (dp_{c_i} '= dp_{c_i} + a imes w_i)
    • (dp_{c_i}' = max dp_k + b imes w_i \, (k ot = c_i))

    在求(max dp_k)上有一个小技巧。我们记录dp值最大的两种颜色,那么,其中至少有一个不是(c_i)。这样,对于每次询问,我们多能(O(n))地完成dp。

    时间复杂度(O(nq))

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    template <typename tp>
    inline void read(tp& x) {
      x = 0;
      char tmp;
      bool key = 0;
      for (tmp = getchar() ; !(tmp>='0'&&tmp<='9') ; tmp=getchar())
        key = (tmp == '-');
      for ( ; tmp >= '0' && tmp <= '9' ; tmp = getchar())
        x = (x<<1) + (x<<3) + tmp - '0';
      if (key) x = -x;
    }
    const int N = 100010, INF = 0x3f3f3f3f3f3f3f3f;
    int n,q,dp[N],c[N],val[N],a,b,mx,mx1,pm,pm1,vis[N];
    signed main() {
      read(n), read(q);
      for (int i = 1 ; i <= n ; ++ i)
        read(val[i]);
      for (int i = 1 ; i <= n ; ++ i)
        read(c[i]);
      for (int i = 1 ; i <= q ; ++ i) {
        read(a), read(b);
        for (int j = 1 ; j <= n ; ++ j)
          dp[j] = 0, vis[j] = 0;
        mx = mx1 = 0;
        pm = pm1 = 0;
        for (int j = 1 ; j <= n ; ++ j) {
          int x = -INF,y = -INF,z = -INF,v;
          if (pm != c[j])
    	x = b * val[j] + mx;
          if (pm1 != c[j])
    	y = b * val[j] + mx1;
          if (vis[c[j]])
    	z = val[j] * a + dp[c[j]];
          v = max(x,y); v = max(v,z);
          if (!vis[c[j]]) dp[c[j]] = v;
          else dp[c[j]] = max(dp[c[j]],v);
          vis[c[j]] = 1;
          if (v == dp[c[j]]) {
    	if (pm == c[j]) mx = v;
    	else if (v > mx) {
    	  mx1 = mx;
    	  pm1 = pm;
    	  mx = v;
    	  pm = c[j];
    	} else if (v > mx1) {
    	  mx1 = v;
    	  pm1 = c[j];
    	}
          }
        }
        int ans = 0;
        for (int j = 1 ; j <= n ; ++ j)
          if (vis[j]) ans = max(ans,dp[j]);
        cout << ans << endl;
      }
      return 0;
    }
    

    CF212C. Cowboys

    题意:(n)个人站成一个圆圈。每个人要么面向顺时针,要么面向逆时针。前者用A来表示,后者用B表示。如果两个人面对面地站着,那么,在下一秒他们都对改变自己面朝的方向。给出一秒后的状态,问在这一秒有多少种可能的站法。

    (n leq 100)

    考虑dp。令dp[i,a]为放到第(i)位且第(i)位是(a)的方案数。分类讨论一下就能转移了。

    此外,还要讨论环的开头和结尾是否面对面。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N = 110;
    int dp[N][2],n,val[N],ans;
    char s[N];
    signed main() {
      scanf("%s",s+1);
      n = strlen(s+1);
      for (int i = 1 ; i <= n ; ++ i)
        val[i] = (s[i] == 'A');
      dp[1][val[1]] = 1;
      dp[0][0] = 1;
      for (int i = 2 ; i <= n ; ++ i) {
        if (val[i]) {
          dp[i][1] += dp[i-1][0] + dp[i-1][1];
          if (!val[i-1] && i != 2) dp[i][0] += dp[i-2][0] + dp[i-2][1];
        } else dp[i][0] += dp[i-1][0];
        // printf("%lld:(%lld,%lld)
    ",i,dp[i][0],dp[i][1]);
      }
      if (val[1]) ans += dp[n][0] + dp[n][1];
      else {
        ans += dp[n][0];
        if (val[2]) {
          memset(dp,0,sizeof dp);
          dp[2][0] = 1;
          for (int i = 3 ; i <= n ; ++ i) {
    	if (val[i]) {
    	  dp[i][1] += dp[i-1][0] + dp[i-1][1];
    	  if (!val[i-1] && i != 2) dp[i][0] += dp[i-2][0] + dp[i-2][1];
    	} else dp[i][0] += dp[i-1][0];
          }
          ans += dp[n][0] + dp[n][1];
        }
      }
      memset(dp,0,sizeof dp);
      if (val[1] == 1 && val[n] == 0) {
        dp[1][0] = 1;
        for (int i = 2 ; i < n ; ++ i) {
          if (val[i]) {
    	dp[i][1] += dp[i-1][0] + dp[i-1][1];
    	if (!val[i-1]) dp[i][0] += dp[i-2][0] + dp[i-2][1];
          } else dp[i][0] += dp[i-1][0];
        }
        ans += dp[n-1][0] + dp[n-1][1];
      }
      cout << ans << endl;
      return 0;
    }
    

    CF160D. Edges in MST

    题意:有一个(n)个点,(m)条边的联通图,边有边权。求每一条边与这个图的最小生成树的关系(在所有最小生成树中;在某些最小生成树中;不在任何最小生成树中)。

    (n, m leq 10^5)

    考虑kruskal的运算过程。那么,我们显然要对边权相等的边合起来考虑。

    假设当前有若干个联通块,则连接同一个联通块的边,显然不会在任何最小生成树中。(加入它就会形成环,且环上其他边的边权都小于它)

    剩下的边至少出现在一个最小生成树中。若某条边出现在所有最小生成树中,则加入所有权值小于等于它的边后,它不在任何环上。也就是说,它是桥。因此,我们把目前的联通块缩点,加入这些边后用targan判断是否是桥就可以了。这里还要特判重边。

    时间复杂度(O(n log n))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100010;
    struct edge {
      int a,b,v,id;
      bool operator < (const edge& x) const {
        return v < x.v;
      }
    } ed[N];
    int n,uni[N],m,ans[N];
    vector<int> recv,rece;
    multiset<pair<int,int> > st;
    struct cont {
      int la,b;
    } con[N << 1];
    int tot,fir[N];
    void add(int from,int to) {
      con[++tot] = (cont) {fir[from],to};
      fir[from] = tot;
    }
    int dfn[N],low[N],top,sta[N],col[N],ccnt;
    void dfs(int pos,int fa) {
      sta[low[pos] = dfn[pos] = ++top] = pos;
      for (int i = fir[pos] ; i ; i = con[i].la) {
        if (col[con[i].b] || con[i].b == fa) continue;
        if (!dfn[con[i].b]) {
          dfs(con[i].b,pos);
          low[pos] = min(low[con[i].b],low[pos]);
        } else low[pos] = min(low[pos],dfn[con[i].b]);
      }
      if (low[pos] == dfn[pos]) {
        ++ ccnt;
        while (top >= dfn[pos])
          col[sta[top--]] = ccnt;
      }
    }
    void init() {
      for (int i = 0 ; i < (int)recv.size() ; ++ i) {
        int pos = recv[i];
        fir[pos] = dfn[pos] = low[pos] = col[pos] = 0;
      }
      recv.clear();
      tot = ccnt = top = 0;
      rece.clear();
    }
    int get_fa(int pos) {
      return uni[pos] != pos ? uni[pos] = get_fa(uni[pos]) : pos;
    }
    int main() {
      int x,y,z;
      scanf("%d%d",&n,&m);
      for (int i = 1 ; i <= m ; ++ i) {
        scanf("%d%d%d",&x,&y,&z);
        ed[i] = (edge) {x,y,z,i};
      }
      sort(ed+1,ed+m+1);
      for (int i = 1 ; i <= n ; ++ i)
        uni[i] = i;
      for (int i = 1 ; i <= m ; ++ i) {
        int tmp = ed[i].v, rec = i;
        st.clear();
        for ( ; i <= m && ed[i].v == tmp ; ++ i) {
          if (get_fa(ed[i].a) == get_fa(ed[i].b))
    	ans[ed[i].id] = -1;
          else {
    	int x = get_fa(ed[i].a), y = get_fa(ed[i].b);
    	if (x > y) swap(x,y);
    	st.insert(make_pair(x,y));
    	recv.push_back(x);
    	recv.push_back(y);
          }
        }
        i --;
        for (int j = rec ; j <= i ; ++ j) {
          if (get_fa(ed[j].a) != get_fa(ed[j].b)) {
    	int x = get_fa(ed[j].a), y = get_fa(ed[j].b);
    	if (x > y) swap(x,y);
    	add(x,y);
    	add(y,x);
    	if (st.count(make_pair(x,y)) > 1) {
    	  ans[ed[j].id] = 2;
    	} else rece.push_back(j);
          }
        }
        for (int j = 0 ; j < (int)recv.size() ; ++ j)
          if (!dfn[recv[j]]) dfs(recv[j],0);
        for (int j = 0 ; j < (int)rece.size() ; ++ j) {
          int k = rece[j];
          if (col[get_fa(ed[k].a)] != col[get_fa(ed[k].b)])
    	ans[ed[k].id] = 1;
          else ans[ed[k].id] = 2;
        }
        init();
        for ( ; rec <= i ; ++ rec) {
          int x = ed[rec].a, y = ed[rec].b;
          x = get_fa(x), y = get_fa(y);
          if (x != y)
    	uni[x] = y;
        }
      }
      for (int i = 1 ; i <= m ; ++ i) {
        if (ans[i] == -1) puts("none");
        else if (ans[i] == 1) puts("any");
        else if (ans[i] == 2) puts("at least one");
        else assert(0);
      }
      return 0;
    }
    

    CF372B. Counting Rectangles is Fun

    题意:给出一个(n imes m)的01矩阵,有(q)次询问,问它的一个子矩阵中有多少个不包含1的子矩阵。

    (n,m leq 40, \, q leq 3 imes 10^5)

    关键就在于一个小技巧,预处理时,我们枚举所有子矩阵,只计算出底与它的底在同一条直线上的子矩阵个数。对这个做前缀和就能得到答案。而通过使用单调栈,很容易把预处理复杂度优化到(O(n^5))(O(n^4))

    时间复杂度(O(n^5 + m))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 45;
    int a[N][N],n,m,q,top,cur,ans[N][N][N][N];
    char tmp[N];
    struct data {
      int l,r,v;
    } sta[N];
    int main() {
      int x,y,z,d;
      scanf("%d%d%d",&n,&m,&q);
      for (int i = 1 ; i <= n ; ++ i) {
        scanf("%s",tmp+1);
        for (int j = 1 ; j <= m ; ++ j)
          a[i][j] = tmp[j] - '0';
      }
      for (int i = 1 ; i <= n ; ++ i)
        for (int j = i ; j <= n ; ++ j) {
          for (int k = 1 ; k <= m ; ++ k) {
    	top = 0;
    	cur = 0;
    	for (int p = k ; p <= m ; ++ p) {
    	  int tmp = j - i + 1;
    	  for (int t = i ; t <= j ; ++ t)
    	    if (a[t][p] == 1) tmp = j - t;
    	  data tp = (data) {p,p,tmp};
    	  while (sta[top].v >= tp.v && top) {
    	    tp.l = sta[top].l;
    	    cur -= (sta[top].r - sta[top].l + 1) * sta[top].v;
    	    top --;
    	  }
    	  sta[++top] = tp;
    	  cur += (tp.r - tp.l + 1) * tp.v;
    	  ans[i][j][k][p] = ans[i][j][k][p-1] + cur;
    	}
          }
        }
      for (int i = 1 ; i <= n ; ++ i)
        for (int k = 1 ; k <= m ; ++ k)
          for (int p = k ; p <= m ; ++ p)
    	for (int j = i ; j <= n ; ++ j)
    	  ans[i][j][k][p] += ans[i][j-1][k][p];
      for (int i = 1 ; i <= q ; ++ i) {
        scanf("%d%d%d%d",&x,&y,&z,&d);
        printf("%d
    ",ans[x][z][y][d]);
      }
      return 0;
    }
    

    CF510E. Fox And Dinner

    题意:有(n)个元素,每个的权值为(a_i)。你需要把它们分成若干组,每一组的元素排成一个环,且满足:

    • 每一组都至少有3个元素。
    • 每一组的环上,每一对相邻元素的权值和为奇质数。

    要求判断是否有解,若有解则输出任意一组解。

    (n leq 200, \, a_i leq 10^4)

    显然,两个元素如果相加为奇数,那么一定是一奇一偶。因此,我们可以把元素按奇偶性分组,然后依照相加能否为奇质数连边。这样,问题就变成了二分图的环覆盖。

    设一个结点在二分图上的度数为与它匹配的边数。考虑一个点如果在环上,那么它的度数为2。可以发现,存在方案使二分图上的每一个结点度数为2,等价于存在一个环覆盖的方案。这个结论必要性显然,而在充分性上,若有结点点不在环上,则一定存在结点的度数小于2;若有结点在多个环上,则一定存在结点的度数大于2。

    让每个点的度数都变成2,这个可以用最大流解决。至于输出方案,只要在图上dfs就可以了。

    时间复杂度(O(n^4 + m)),其中(m)(a_i)的权值范围。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 610, MAX = 20000, INF = 0x3f3f3f3f;
    struct edge {
      int la,b,cap;
    } con[N * N];
    int tot=1,fir[N];
    void add(int from,int to,int capc) {
      con[++tot] = (edge) {fir[from],to,capc};
      fir[from] = tot;
      con[++tot] = (edge) {fir[to],from,0};
      fir[to] = tot;
    }
    int cur[N], dis[N], n, st, en, vis[N];
    int dfs(int pos,int imp) {
      if (pos == en || (!imp)) return imp;
      int expo = 0, tmp;
      for (int &i = cur[pos] ; i ; i = con[i].la) {
        if (dis[con[i].b] == dis[pos] + 1) {
          tmp = dfs(con[i].b,min(imp,con[i].cap));
          con[i].cap -= tmp;
          con[i^1].cap += tmp;
          expo += tmp;
          imp -= tmp;
          if (!imp) break;
        }
      }
      return expo;
    }
    bool bfs() {
      static queue<int> q;
      while (!q.empty()) q.pop();
      memset(dis,0,sizeof dis);
      for (int i = 1 ; i <= n ; ++ i)
        cur[i] = fir[i];
      dis[st] = 1;
      q.push(st);
      for (int pos ; !q.empty() ; q.pop()) {
        pos = q.front();
        for (int i = fir[pos] ; i ; i = con[i].la) {
          if (con[i].cap && (!dis[con[i].b])) {
    	dis[con[i].b] = dis[pos] + 1;
    	q.push(con[i].b);
          }
        }
      }
      if (!dis[en]) return 0;
      return 1;
    }
    int a[N],isp[MAX + 10], pri[MAX], pcnt, ans, cnt;
    vector<int> rec[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;
        }
      }
    }
    void sdf(int pos,int fa) {
      rec[cnt].push_back(pos);
      vis[pos] = 1;
      for (int i = fir[pos] ; i ; i = con[i].la) {
        if (con[i].b <= n-2 && ((con[i].cap == 0 && (i&1) == 0) || (con[i].cap == 1 && (i&1) == 1))) {
          if (con[i].b == fa || vis[con[i].b]) continue;
          sdf(con[i].b,pos);
        }
      }
    }
    int main() {
      scanf("%d",&n);
      for (int i = 1 ; i <= n ; ++ i)
        scanf("%d",&a[i]);
      st = ++n;
      en = ++n;
      prework();
      for (int i = 1 ; i <= n-2 ; ++ i) {
        if (a[i]&1) {
          add(st,i,2);
          for (int j = 1 ; j <= n-2 ; ++ j)
    	if (!isp[a[i] + a[j]]) add(i,j,1);
        } else add(i,en,2);
      }
      while (bfs())
        ans += dfs(st,INF);
      if (ans < n - 2) puts("Impossible");
      else {
        for (int i = 1 ; i <= n - 2 ; ++ i) if (!vis[i]) {
    	++ cnt;
    	sdf(i,0);
          }
        printf("%d
    ",cnt);
        for (int i = 1 ; i <= cnt ; ++ i) {
          printf("%d ",(int)rec[i].size());
          for (int j = 0 ; j < (int)rec[i].size() ; ++ j)
    	printf("%d ",rec[i][j]);
          puts("");
        }
      }
      return 0;
    }
    

    小结:总会卡在几个小技巧上……应该是做题经验不够丰富。
  • 相关阅读:
    微信小程序 单选按钮 最佳
    微信小程序 单选按钮的实现
    微信小程序 单选框实现
    Java Code To Create Pyramid and Pattern
    Java language
    npm Err! Unexpected end of JSON input while parsing near
    Node.js Express FrameWork Tutorial
    Higher-Order Function Examples
    Create First HTTP Web Server in Node.js: Complete Tutorial
    Node.js NPM Tutorial: Create, Publish, Extend & Manage
  • 原文地址:https://www.cnblogs.com/cly-none/p/9299353.html
Copyright © 2011-2022 走看看