zoukankan      html  css  js  c++  java
  • 2020.11.13 “考试”

    小结

    挂分小天才~

    • 两数相乘转化为两数 (log) 函数相加的操作。
    • 二进制分组求多源最短路。
    • 注意卡常……

    A

    给出一个长度为 (n) 的数列,要求选出若干个数,且选出的数不能相邻,求选出数的最大乘积,对 (10^9+9) 取模。
    (n,a_ile{10^6})

    看到马上想到用 DP。
    可以用 (f_{i,1/0}) 来表示前 (i) 个数中,第 (i) 个数选或不选获得的最大价值。但是其实后面 (0/1) 这一维是不需要的,直接用 (f_{i}) 表示前 (i) 个数获得的最大价值即可。

    那么显然转移方程有:

    [f_{i}=max(f_{i-1},f_{i-2} imes{a_i}) ]

    初态为 (f_{0}=0,f_{1}=a_1),目标为 (f_{n})

    但是直接相乘会爆掉 ( ext{long long}),因此要用到一个小技巧:将两数相乘转化为两数的 (log) 函数相加。

    这样就可以过掉这道题了。

    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int A = 1e6 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 9;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar();
      int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    ll f[A];
    int n, val[A];
    double lgsum[A], lg[A];
    
    int main() {
      freopen("jsnk.in", "r", stdin);
      freopen("jsnk.out", "w", stdout);
      n = read();
      for (int i = 1; i <= n; i++) {
        val[i] = read();
        lg[i] = log(val[i]);
      }
      f[0] = 1;
      lgsum[1] = lg[1], f[1] = val[1];
      for (int i = 2; i <= n; i++) {
        lgsum[i] = lgsum[i - 1], f[i] = f[i - 1];
        if (lgsum[i - 2] + lg[i] > lgsum[i]) {
          lgsum[i] = lgsum[i - 2] + lg[i];
          f[i] = 1ll * f[i - 2] * val[i] % mod;
        }
      }
      cout << f[n] << '
    ';
      return 0;
    }
    

    B

    有一张 (n) 个点 (m) 条边的无向图,边有边权,给出 (k) 个点,求这 (k) 个点中最短路最短的两点之间的最短路。
    多组数据。
    (Tle10,n,mle100000,边权le1000)

    本地电脑真慢……被卡了 30 分,传到洛谷上就稳 A……
    想喷出题人卡 SPFA,不过我喜欢写 Dijkstra qwq

    二进制分组最短路的板子题。

    因为这 (k) 个点的编号都是不同的,所以每个点编号的二进制表示也一定不同。枚举 (k) 的二进制的每一位,根据每个关键点的编号这一位是否为 1 分组,将编号这一位为 1 的数的最短路设为 0,然后丢到堆中,之后跑最短路。然后判断编号这一位为 0 的点的最短路,更新答案即可。

    /*
    多测不清空爆零两行泪
    */
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int A = 1e5 + 11;
    const int B = 2e5 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar();
      int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    struct edge {
      int to, nxt, val;
    } e[B << 1];
    int n, m, k, pos[A], vis[A], cnt, head[A], dis[A], ans = inf;
    
    struct node {
      int x, y;
      bool operator < (const node &b) const {
        return y > b.y;
      }
    };
    
    inline void add(int from, int to, int val) {
      e[++cnt].to = to;
      e[cnt].val = val;
      e[cnt].nxt = head[from];
      head[from] = cnt;
    }
    
    inline void dijk(int bit) {
      priority_queue <node> Q;
      memset(dis, inf, sizeof(dis));
      memset(vis, 0, sizeof(vis));
      for (int i = 1; i <= k; i++) {
        if ((1 << bit) & i) {
          dis[pos[i]] = 0;
          Q.push((node){ pos[i], 0});
        } 
      }
      while (!Q.empty()) {
        int x = Q.top().x;
        Q.pop();
        if (vis[x]) continue;
        vis[x] = 1;
        for (int i = head[x]; i; i = e[i].nxt) {
          int to = e[i].to, val = e[i].val;
          if (dis[x] + val < dis[to]) {
            dis[to] = dis[x] + val;
            if (!vis[to]) Q.push((node) { to, dis[to] });
          }
        }
      }
    }
    
    inline void solve() {
      memset(head, 0, sizeof(head));
      cnt = 0;
      n = read(), m = read(), k = read();
      for (int i = 1; i <= m; i++) {
        int x = read(), y = read(), val = read();
        add(x, y, val), add(y, x, val);
      }
      int ans = inf;
      for (int i = 1; i <= k; i++) pos[i] = read();
      for (int i = 0; (1 << i) <= k; i++) {
        dijk(i);
        for (int j = 1; j <= k; j++) {
          if ((1 << i) & j) continue;
          if (dis[pos[j]] < ans) ans = dis[pos[j]];
        }
      }
      printf("%d
    ", ans == inf ? -1 : ans);
    }
    
    int main() {
      freopen("muzan.in", "r", stdin);
      freopen("muzan.out", "w", stdout);
      int T = read();
      while (T--) solve();
      return 0;
    }
    

    C

    给定一只由 (0,1,2) 构成的数列,求满足三种权值的数量不多于区间长度一半的区间的个数。
    (nle5 imes10^6)

    30pts

    记录区间内 0,1,2 的个数,然后判断每个区间是否合法。

    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int A = 1e6 + 11;
    const int B = 1e5 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar();
      int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    char s[B];
    int n, sum[B][3], a[B], b[B], c[B];
    
    void sub1() {
      for (int i = 1; i <= n; i++) {
        sum[i][0] = sum[i - 1][0];
        sum[i][1] = sum[i - 1][1];
        sum[i][2] = sum[i - 1][2];
        if (s[i] == '0') sum[i][0]++;
        else if (s[i] == '1') sum[i][1]++;
        else if (s[i] == '2') sum[i][2]++;
      }
      ll ans = 0;
      for (int i = 1; i <= n; i++) {
        for (int j = i; j >= 1; j--) {
          if (sum[i][0] - sum[j - 1][0] > (i - j + 1) / 2) continue;
          if (sum[i][1] - sum[j - 1][1] > (i - j + 1) / 2) continue;
          if (sum[i][2] - sum[j - 1][2] > (i - j + 1) / 2) continue;      
          ans++;
        }
      }
      cout << ans << '
    ';
    }
    
    int main() {
      freopen("dokuso.in", "r", stdin);
      freopen("dokuso.out", "w", stdout);
      n = read();
      scanf("%s", s + 1);
      return sub1(), 0;
      return 0;
    }
    

    40pts

    (0) 的情况为例,(1,2) 同理。记 (sum_i)(a_{1sim{i}})(0) 的个数。那么一个区间 ((j,i]) 合法当且仅当 (sum_i-sum_jledfrac{i-j}{2}),转化一下就是 (2sum_i-ile2sum_j-j)

    (X_i=2sum_i-i),那么对于 (0) 这个限制就是要求对于每一个 (i) 有多少个 (j) 满足 (j<i)(X_jge{X_i})

    对于 (1)(2) 同样求出对应的 (Y_i,Z_i)。那么最后的限制就是对于每一个 (i)(j<i)(X_jge{X_i}land{Y_jge{Y_i}}land{Z_j}ge{Z_i}) 的数量。

    经典四维偏序问题,可以用 cdq 套 cdq 解决,码 100+ 行得到十分,真是太棒啦!

    60pts

    去写 40 分的显然是神仙。
    考虑将问题转化,本来要求的是对于每一个 (i)(j<i)(X_jge{X_i}land{Y_jge{Y_i}}land{Z_j}ge{Z_i}) 的个数,但是发现我们可以用总区间数减去不合法的区间数,即 (dfrac{n(n+1)}{2}-j<iland((X_j<{X_i})land({Y_j<{Y_i})}land({Z_j}<{Z_i})))

    又发现其实后面这三种是没有交集的,因为不可能有两种数超过区间的一半。

    所以 (j<iland((X_j<{X_i})land({Y_j<{Y_i})}land({Z_j}<{Z_i}))) 的区间数量 等于 (|j<iland(X_j<{X_i})|+|j<iland({Y_j<{Y_i})}|+|j<iland({Z_j}<{Z_i})|)

    用树状数组求出来,然后求和即可。

    时间复杂度 (O(nlog{n}))

    100pts

    根据公式 (X_i=2sum_{i}-i) 可得,(|X_i-X_{i-1}|=1)。因此把树状数组改成暴力就可以过这道题。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define ll long long
    using namespace std;
    const int N=5000010;
    int a[N],sum[N],n,c[N<<1];
    ll ss(int x)
    {
    	for(int i=1;i<=n;++i)
    	{
    		if(a[i]==x)sum[i]=1;
    		else sum[i]=-1;
    	}
    	int minn=0;
    	for(int i=1;i<=n;++i)sum[i]+=sum[i-1],minn=min(minn,sum[i]);
    	for(int i=0;i<=n;++i)sum[i]-=minn;
    	int id=0,h=0;
    	ll res=0;
    	memset(c,0,sizeof(c));
    	for(int i=0;i<=n;++i)
    	{
    		if(sum[i]>id)h+=c[sum[i-1]];
    		else h-=c[sum[i]];
    		res+=h;
    		id=sum[i];
    		c[sum[i]]++;
    	}
    	return res;
    }
    int main()
    {
    	freopen("dokuso.in","r",stdin);
    	freopen("dokuso.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)scanf("%1d",&a[i]);
    	ll ans=1ll*n*(n+1)/2;
    	cout<<ans-ss(0)-ss(1)-ss(2);
    	return 0;
    }
    
  • 相关阅读:
    url向视图函数传递参数
    创建django项目
    进度百分比
    【转藏】Makefile学习
    IT人的自我导向型学习:学习的4个层次
    SZ第二次找工作--笔试汇总
    正则表达式 (re包)——python(快餐)
    Python-快速学习
    Vim的使用
    Vim Python
  • 原文地址:https://www.cnblogs.com/loceaner/p/13971516.html
Copyright © 2011-2022 走看看