• 「NOI Online Round2」 题解


    前言

    没脑子又被吊打了,生气(T2签到题写了2.5h)。

    赶紧订正。

    A

    不妨设(p_1<p_2),两种颜色对应(1,2)。容易发现既是(p_1)倍数有是(p_2)倍数的点肯定染成(2),因为前后都是(1)。由于(>lcm)的情况和([1, lcm])一样,只需考虑([1,lcm])。由于(1)十分密集,影响答案的段一定是连续的(1)

    于是我们得找到(y p_2 < x p_1 < (x + k - 1) p_1 < (y+1) p_2),其中(xp_1-yp_2)得尽量小,(k)是极大值。

    根据裴蜀定理,(xp_1-yp_2)最小值是(gcd(p_1,p_2))

    最长连续段长度就是(dfrac{p_2 - 1 - gcd(p_1, p_2)}{p_1} + 1)

    #include <algorithm>
    #include <cstdio>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 50, mod = 1e9 + 7;
    int p1, p2, k;
    int gcd(int a, int b) {
       return !b ? a : gcd(b, a % b);
    }
    int main() {
       // freopen("color.in", "r", stdin);
       // freopen("color.out", "w", stdout);
       int t; scanf("%d", &t);
       while(t --) {
          scanf("%d%d%d", &p1, &p2, &k);
          if(p1 > p2) swap(p1, p2);
          if(k == 1) puts("No");
          else if(p1 == p2) puts("Yes");
          else {
             int g = gcd(p1, p2);
             puts((p2 - 1 - g) / p1 + 1 < k ? "Yes" : "No");
          }
       }
       fclose(stdin); fclose(stdout);
       return 0;
    }
    

    B

    注意到((x+1)^2=x^2+(2x+1)),我们对于每个元素都考虑由他产生的(2x+1)对答案的贡献。

    具体地,我们令(p_i)表示(a_i)上一次出现的位置(没有出现等于(0)),(nxt_i)表示下一次出现的位置(没有出现等于(n+1)),那对于位置(u),我们把答案加上(sum_{i = p_u}^u (2 c(i,u) - 1)(n-u+1)),其中(c(l,r))表示([l,r])中不同数的个数。我们记(q(p_u,u)=(n-u+1)sum_{i = p_u}^u c(i,u)),考虑求这个(q)

    然后考虑每个位置对这些(q)询问的贡献。对于位置(u),他对(q(l,r))有贡献,当且仅当(l leq u, uleq r < nxt_u),并且贡献是((n-r+1)(u + 1 - l))。拆一下就是((u + 1)(n - r + 1) - l (n-r+1)),那我们把询问按(l)排序(你也可以通过nxt数组来免去排序),然后(u=1 ightarrow n),把(lleq u)(q)加入(2)个树状数组的(r)位置,这两个树状数组第一个维护(n-r+1),第二个维护(l(n-r+1))。然后对于每个(u)通过在树状数组上询问([u,nxt_u))把贡献加进去就行。

    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <queue>
    #define pb push_back
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 50, mod = 1e9 + 7;
    void upd(int &x, const int &y) {
       (x += y) >= mod ? x -= mod : 0;
    }
    int n, ans1, ans2, a[N], pre[N], p[N], nxt[N];
    struct uni {
    
    int a[N];
    void build(int *b, int n) {
       for(int i = 1; i <= n; i ++) a[i] = b[i];
       sort(a + 1, a + n + 1);
       for(int i = 1; i <= n; i ++) {
          b[i] = lower_bound(a + 1, a + n + 1, b[i]) - a;
       }
    }
    
    } uq;
    struct Bit {
    
    int bit[N];
    void add(int u, int val) {
       for(; u <= n; u += u & (-u)) {
          upd(bit[u], val);
       }
    }
    int qry(int u) {
       int ans = 0;
       for(; u >= 1; u &= u - 1) {
          upd(ans, bit[u]);
       }
       return ans;
    }
    int qry(int l, int r) {
       return (qry(r) - qry(l - 1) + mod) % mod;
    }
    
    } b0, b1;
    struct Node {
       int l, r;
       bool operator < (const Node &b) const {
          return l < b.l;
       }
    } an[N];
    int main() {
       // freopen("sequence.in", "r", stdin);
       // freopen("sequence.out", "w", stdout);
       scanf("%d", &n);
       for(int i = 1; i <= n; i ++) {
          scanf("%d", a + i);
       }
       uq.build(a, n);
       for(int i = 1; i <= n; i ++) {
          p[i] = pre[a[i]]; pre[a[i]] = i;
          upd(ans1, (i - p[i]) * (n - i + 1ll) % mod);
       }
       fill(pre + 1, pre + n + 1, n + 1);
       for(int i = n; i >= 1; i --) {
          nxt[i] = pre[a[i]]; pre[a[i]] = i;
          an[i] = (Node) {p[i] + 1, i};
       }
       sort(an + 1, an + n + 1);
       int ans3 = 0, cur = 0;
       for(int i = 1; i <= n; i ++) {
          while(cur < n && an[cur + 1].l <= i) {
             cur ++;
             b0.add(an[cur].r, n - an[cur].r + 1);
             b1.add(an[cur].r, (n - an[cur].r + 1ll) * an[cur].l % mod);
          }
          upd(ans2, ((i + 1ll) * b0.qry(i, nxt[i] - 1) % mod - b1.qry(i, nxt[i] - 1) + mod) % mod);
       }
       ans1 = ((2ll * ans2 - ans1) % mod + mod) % mod;
       printf("%d
    ", ans1);
       fclose(stdin); fclose(stdout);
       return 0;
    }
    

    C

    我们用(f(u))表示标记(u)对祖先 - 后代关系结点,剩下(m-u)对随便配对的方案。(g(u))表示恰好有(u)对祖先 - 后代的方案数,题目要求(g(0))(g(m))

    [f(n)=sum_{i = n}^m {ichoose n} g(i) ]

    根据二项式反演,

    [g(n) = sum_{i = n}^m {ichoose n} (-1)^{n - i} f(i) ]

    这个(f(n))很好求,(dp[u][x])表示(u)子树标记(x)个祖先 - 后代关系的方案数(没标记的最后考虑,把阶乘乘上去)。先把子树背包合并,然后考虑选(u)

    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #define pb push_back
    using namespace std;
    typedef long long ll;
    const int N = 5100, mod = 998244353;
    int n, ans, fac[N], fav[N], dp[N][N], sz[2][N];
    char s[N];
    vector<int> G[N];
    int C(int n, int m) {
       return 1ll * fac[n] * fav[m] % mod * fav[n - m] % mod;
    }
    int qpow(int a, int b) {
       int ans = 1;
       for(; b >= 1; b >>= 1, a = (ll) a * a % mod)
          if(b & 1) ans = (ll) ans * a % mod;
       return ans;
    }
    #define full(u) min(sz[0][u], sz[1][u])
    void dfs(int u, int fa = 0) {
       sz[0][u] = s[u] == 0; sz[1][u] = s[u] == 1; dp[u][0] = 1;
       for(int i = 0; i < (int) G[u].size(); i ++) {
          int v = G[u][i];
          if(v != fa) {
             dfs(v, u);
             static int t[N];
             fill(t, t + full(u) + full(v) + 1, 0);
             for(int j = full(u); j >= 0; j --) if(dp[u][j]) {
                for(int k = full(v); k >= 0; k --) if(dp[v][k]) {
                   (t[j + k] += 1ll * dp[u][j] * dp[v][k] % mod) %= mod;
                }
             }
             copy(t, t + full(u) + full(v) + 1, dp[u]);
             sz[0][u] += sz[0][v]; sz[1][u] += sz[1][v];
          }
       }
       for(int i = full(u) - 1; i >= 0; i --) {
          (dp[u][i + 1] += 1ll * dp[u][i] * (sz[s[u] ^ 1][u] - i) % mod) %= mod;
       }
    }
    int main() {
       // freopen("match.in", "r", stdin);
       // freopen("match.out", "w", stdout);
       scanf("%d%s", &n, s + 1); int m = n >> 1;
       for(int i = 1; i <= n; i ++) s[i] -= '0';
       fac[0] = 1;
       for(int i = 1; i <= m; i ++) fac[i] = 1ll * fac[i - 1] * i % mod;
       fav[m] = qpow(fac[m], mod - 2);
       for(int i = m; i >= 1; i --) fav[i - 1] = 1ll * fav[i] * i % mod;
       for(int i = 1; i < n; i ++) {
          int u, v;
          scanf("%d%d", &u, &v);
          G[u].pb(v); G[v].pb(u);
       }
       dfs(1);
       for(int i = 0; i <= m; i ++) {
          dp[1][i] = 1ll * dp[1][i] * fac[m - i] % mod;
       }
       for(int i = 0; i <= m; i ++) {
          int res = 0;
          for(int j = i; j <= m; j ++) {
             if((j - i) & 1) (res += mod - 1ll * dp[1][j] * C(j, i) % mod) %= mod;
             else (res += 1ll * dp[1][j] * C(j, i) % mod) %= mod;
          }
          printf("%d
    ", res);
       }
       fclose(stdin); fclose(stdout);
       return 0;
    }
    
  • 相关阅读:
    分布式事务系列--分布式跨库查询解决方案 mysql federated引擎的使用
    【MySQL】跨库join
    实操手册:如何玩转跨库Join?跨数据库实例查询应用实践
    实现数据库的跨库join
    微服务改造中解决跨库问题的思路
    从jar包中加载feignClient
    注入jar包里的对象,用@autowired使用
    使用 IntraWeb (27)
    使用 IntraWeb (26)
    使用 IntraWeb (25)
  • 原文地址:https://www.cnblogs.com/hongzy/p/12774296.html
Copyright © 2020-2023  润新知