zoukankan      html  css  js  c++  java
  • 省选测试50

    A 矩形

    题目大意 : 从n×m点中选k个点在一条直线上的方案数

    • 莫比乌斯反演,把步骤记下来,不然老是忘(我太菜了)

    • 横着和竖着的都好算,斜着的可以分为左斜和右斜,枚举这k个点所在线段所在的矩形,对于一个长宽i,j的矩形,对角线上的点数是gcd(i,j)+1,把两端确定,中间选k-2就不会重复,所以可以得到一个n2的式子(假设n<=m)

    [ans=ninom{m}{k}+minom{n}{k}+2sum_{i=1}^{n}sum_{i=1}^{m}inom{gcd(i,j)-1}{k-2}(n-i)(m-j) ]

    • 对后面的两个求和的那个式子进行莫比乌斯反演

    [egin{align*} ans'&=sum_{i=1}^{n}sum_{j=1}^{m}inom{gcd(i,j)-1}{k-2}(n-i)(m-j) \&= sum_{p=1}^{n}inom{p-1}{k-2}sum_{i=1}^{n}sum_{j=1}^{m}[gcd(i,j)=p](n-i)(m-j) \&= sum_{p=1}^{n}inom{p-1}{k-2}sum_{i=1}^{left lfloor frac{n}{p} ight floor}sum_{j=1}^{left lfloor frac{m}{p} ight floor}[gcd(i,j)=1](n-pi)(m-pj) \&= sum_{p=1}^{n}inom{p-1}{k-2}sum_{i=1}^{left lfloor frac{n}{p} ight floor}sum_{j=1}^{left lfloor frac{m}{p} ight floor}sum_{d|gcd(i,j)}mu (d)(n-pi)(m-pj) \&= sum_{p=1}^{n}inom{p-1}{k-2}sum_{d=1}^{left lfloor frac{n}{p} ight floor}mu (d)sum_{d|i}^{left lfloor frac{n}{p} ight floor}sum_{d|j}^{left lfloor frac{m}{p} ight floor}(n-pi)(m-pj) \&= sum_{p=1}^{n}inom{p-1}{k-2}sum_{d=1}^{left lfloor frac{n}{p} ight floor}mu (d)sum_{i=1}^{left lfloor frac{n}{pd} ight floor}(n-pdi)sum_{j=1}^{left lfloor frac{m}{pd} ight floor}(m-pdj) end{align*}]

    • 接下来正常的话就要设T=pd,然后继续推,不过这道题到这里就可以(O(nln n))做出来了,因为后面两个求和是显然可以用等差数列求和公式优化到O(1)

    • 预处理组合数和莫比乌斯函数(mu (d)),当x有相同质因子,函数值为0,否则是-1的质因子个数次方

    Code

    Show Code
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    const int N = 1e5 + 5, M = 323232323;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    bool v[N];
    int n, m, k, pri[N], tot, mu[N], fac[N], inv[N], ans;
    
    int Pow(int a, int k, int ans = 1) {
        for (; k; k >>= 1, a = 1ll * a * a % M)
            if (k & 1) ans = 1ll * ans * a % M;
        return ans;
    }
    
    void Init(int n) {
        mu[1] = fac[0] = 1;
        for (int i = 2; i < n; ++i) {
            if (!v[i]) pri[++tot] = i, mu[i] = M-1;
            for (int j = 1; j <= tot && i * pri[j] <= n; ++j) {
                v[i*pri[j]] = 1;
                if (i % pri[j] == 0) break;
                mu[i*pri[j]] = M-mu[i];
            }
        }
        for (int i = 1; i <= n; ++i)
            fac[i] = 1ll * fac[i-1] * i % M;
        inv[n] = Pow(fac[n], M - 2);
        for (int i = n; i >= 1; --i)
            inv[i-1] = 1ll * inv[i] * i % M;
    }
    
    int C(int n, int m) {
        return 1ll * fac[n] * inv[m] % M * inv[n-m] % M;
    }
    
    int Cal(int n, int pd) {
        return (-1ll * (n / pd) * (n / pd + 1) / 2 * pd % M + M + 1ll * n / pd * n) % M;
    }
    
    int main() {
        freopen("a.in", "r", stdin);
        freopen("a.out", "w", stdout);
        n = read(); m = read(); k = read();
        if (k == 1) return printf("%lld
    ", 1ll * n * m % M), 0;
        if (n > m) swap(n, m); Init(m);
        for (int p = k-1; p <= n; ++p) {
            int sum = 0;
            for (int d = n / p; d >= 1; --d)
                if ((sum += 1ll * Cal(n, p * d) * Cal(m, p * d) % M * mu[d] % M) >= M) sum -= M;
            if ((ans += 1ll * C(p - 1, k - 2) * sum % M) >= M) ans -= M;
        }
        printf("%lld
    ", (1ll * n * C(m, k) + 1ll * m * C(m, k) + 2 * ans) % M);
        return 0;
    }
    

    B 覆盖

    题目大意 : 一个点覆盖一个区间的代价是到区间两端的距离之差,问在用点最少的情况下覆盖所有线段的最小代价

    • 最少用点好求,按右端点排序,每次给在第一个没选的区间的右端点放一个点

    • 设p[i]表示上述贪心第i个点所放的位置,而在最小代价的情况下第i个点一定会放在[p[i-1]+1,p[i]],于是就可以dp了

    • 设对于每段,f[i]表示这段放在这个点上的最小代价,转移点只在上个段

    • 发现有决策单调性,可以(nlog n)解决

    Code

    Show Code
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    ll s[N], f[N], ans = 1e18;
    int n, b[N], c[N], cnt, p[N];
    
    struct Node {
        int l, r;
    }a[N];
    
    bool operator < (const Node &a, const Node &b) {
        return a.r < b.r;
    }
    
    ll Cal(int l, int r) {
        if (l < b[r-1]) return 1e18;
        int mid = l + r >> 1;
        return (s[mid] - s[l]) - 1ll * (c[mid] - c[l]) * l + 1ll * (c[r] - c[mid]) * r - (s[r] - s[mid]) + f[l];
    }
    
    void Solve(int pl, int pr, int l, int r) {
        if (pl > pr || l > r) return;
        int mid = l + r >> 1, p = pl;
        for (int i = pl; i <= pr; ++i) {
            ll tmp = Cal(i, mid);
            if (tmp < f[mid]) f[mid] = tmp, p = i;
        }
        Solve(pl, p, l, mid-1); Solve(p, pr, mid+1, r); 
    }
    
    int main() {
        freopen("b.in", "r", stdin);
        freopen("b.out", "w", stdout);
        n = read();
        for (int i = 1; i <= n; ++i) {
            int l = read() * 2, r = read() * 2, mid = l + r >> 1;
            a[i] = (Node) {l, r}; b[r] = max(b[r], l);
            s[mid] += mid; c[mid]++;
        }
        sort(a + 1, a + n + 1);
        for (int i = 1; i <= n; ++i)
            if (a[i].l > p[cnt]) p[++cnt] = a[i].r;
        memset(f + 1, 0x3f, (n *= 2) * 8);
        for (int i = 1; i <= n; ++i) {
            b[i] = max(b[i], b[i-1]);
            s[i] += s[i-1]; c[i] += c[i-1];
        }
        for (int i = 1; i <= p[1]; ++i) 
            f[i] = 1ll * c[i] * i - s[i];
        for (int i = 2; i <= cnt; ++i)
            Solve(p[i-2] + 1, p[i-1], p[i-1] + 1, p[i]);
        for (int i = max(p[cnt-1]+1, b[n]); i <= p[cnt]; ++i)
            ans = min(ans, f[i] + (s[n] - s[i]) - 1ll * (c[n] - c[i]) * i);
        printf("%d %lld
    ", cnt, ans);
        return 0;
    }
    

    C 强壮

    题目大意 : 限制dfn序为i的点子树不小于a[i]

    • 题意可真是难懂,dfn不和a[i]放一起说,气死了

    • f[x][i]表示以x为根的子树dfn序最大的链的长度为i的方案数

    • 按dfn从小到大考虑可以接到i子树里的点,一定只能接在最右边的链上,

    • 所以f[x][i]×f[y][j]会对f[x][j+1~i+j-1]有贡献,暴力转移的话是n3

    • 可以看一个值会由谁转移来,发现f[y][j]固定后,f[x][i+j+1]只会由f[x][i~sz[x]]转移,所以做个后缀和就可以优化到n2

    Code

    Show Code
    #include <cstdio>
    #include <vector>
    
    using namespace std;
    const int N = 1e4 + 5, M = 323232323;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    vector<int> to[N];
    int n, a[N], stk[N], tp, f[N][N], sz[N], ans, g[N];
    
    void Dfs(int x) {
        sz[x] = 1; f[x][0] = 1;
        for (int k = 0, y; k < to[x].size(); ++k) {
            Dfs(y = to[x][k]);
            for (int i = sz[x] - 2; i >= 0; --i)
                if ((f[x][i] += f[x][i+1]) >= M) f[x][i] -= M;
            for (int i = 0; i < sz[x]; ++i)
                for (int j = 0; j < sz[y]; ++j)
                    if ((g[i+j+1] += 1ll * f[x][i] * f[y][j] % M) >= M) g[i+j+1] -= M;
            sz[x] += sz[y];
            for (int i = 0; i < sz[x]; ++i)
                f[x][i] = g[i], g[i] = 0;
        }
    }
    
    int main() {
        freopen("c.in", "r", stdin);
        freopen("c.out", "w", stdout);
        n = read(); read(); a[1] = n;
        for (int i = 2; i <= n; ++i) 
            a[i] = i + read() - 1;
        for (int i = n; i >= 1; --i) {
            if (a[i] > n) return puts("0"), 0;
            while (tp && stk[tp] <= a[i]) to[i].push_back(stk[tp--]);
            stk[++tp] = i;
        }
        Dfs(1);
        for (int i = 0; i < n; ++i)
            if ((ans += f[1][i]) >= M) ans -= M;
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    Android 3.0 r1 API中文文档(108) —— ExpandableListAdapter
    Android 3.0 r1 API中文文档(113) ——SlidingDrawer
    Android 3.0 r1 API中文文档(105) —— ViewParent
    Android 中文 API (102)—— CursorAdapter
    Android开发者指南(4) —— Application Fundamentals
    Android开发者指南(1) —— Android Debug Bridge(adb)
    Android中文API(115)——AudioFormat
    Android中文API(116)——TableLayout
    Android开发者指南(3) —— Other Tools
    Android中文API (110) —— CursorTreeAdapter
  • 原文地址:https://www.cnblogs.com/shawk/p/14588813.html
Copyright © 2011-2022 走看看