• 牛客挑战赛30 简要题解


    牛客挑战赛30

    T1

    随便枚举三个位置,然后二维前缀和即可。

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    int sum[505][505];
    int n, a[505];
    ll ans;
     
    int main() {
        int i, j, k;
        scanf("%d", &n);
        for (i = 1; i <= n; ++i) scanf("%d", &a[i]);
        for (i = n; i; --i) {
            for (j = 1; j <= n; ++j) sum[i][j] = sum[i + 1][j];
            for (j = 1; j <= a[i]; ++j) ++sum[i][j];
        }
        for (i = 1; i <= n; ++i)
            for (j = i + 1; j <= n; ++j)
                for (k = j + 1; k <= n; ++k)
                    if (a[i] < a[k] && a[i] < a[j] && a[k] < a[j]) ans += sum[k + 1][a[j] + 1];
        printf("%lld
    ", ans);
        return 0;
    }
    

    T2

    枚举中位数,把小的设为 (-1),大的设为 (1),然后前缀和找为 (0) 的区间。
    被肥鸡卡常了

    //time limit exceeded
    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    const int maxn(10005);
    const int mod(1e9 + 7);
     
    inline void Inc(int &x, const int y) {
        x = x + y >= mod ? x + y - mod : x + y;
    }
     
    inline void Dec(int &x, const int y) {
        x = x - y < 0 ? x - y + mod : x - y;
    }
     
    inline int Add(int x, const int y) {
        return x + y >= mod ? x + y - mod : x + y;
    }
     
    inline int Sub(int x, const int y) {
        return x - y < 0 ? x - y + mod : x - y;
    }
     
    inline int Pow(ll x, int y) {
        ll ret = 1;
        for (; y; y >>= 1, x = x * x % mod)
            if (y & 1) ret = ret * x % mod;
        return ret;
    }
     
    int n, p[maxn], pw[maxn], pw2[maxn], t[maxn << 1], ans, s[maxn];
     
    int main() {
        int i, j, ret;
        scanf("%d%d", &n, &pw[1]), pw[0] = 1;
        for (i = 1; i <= n; ++i) scanf("%d", &p[i]);
        for (i = 2; i <= n; ++i) pw[i] = (ll)pw[i - 1] * pw[1] % mod;
        for (i = 0; i <= n; ++i) pw2[i] = Pow(pw[i], n);
        for (i = 1; i <= n + n; ++i) {
            for (j = 1; j <= n; ++j)
                if (p[j] + p[j] < i) s[j] = -1;
                else if (p[j] + p[j] > i) s[j] = 1;
                else s[j] = 0;
            t[maxn] = 1, ret = 0;
            for (j = 1; j <= n; ++j) {
                s[j] += s[j - 1];
                Inc(ret, (ll)t[maxn + s[j]] * pw[j] % mod);
                Inc(t[s[j] + maxn], pw2[j - 1]);
            }
            Inc(ans, (ll)ret * i % mod);
            for (j = 1; j <= n; ++j) t[s[j] + maxn] = 0;
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    T3

    枚举一个根然后树形 (DP),转移是一个组合数。
    优化的话就是换根 (DP)

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    const int maxn(1e5 + 5);
    const int mod(998244353);
     
    inline void Inc(int &x, const int y) {
        x = x + y >= mod ? x + y - mod : x + y;
    }
     
    inline void Dec(int &x, const int y) {
        x = x - y < 0 ? x - y + mod : x - y;
    }
     
    inline int Add(int x, const int y) {
        return x + y >= mod ? x + y - mod : x + y;
    }
     
    inline int Sub(int x, const int y) {
        return x - y < 0 ? x - y + mod : x - y;
    }
     
    inline int Pow(ll x, int y) {
        ll ret = 1;
        for (; y; y >>= 1, x = x * x % mod)
            if (y & 1) ret = ret * x % mod;
        return ret;
    }
     
    struct Edge {  int to, next;  };
     
    int n, first[maxn], size[maxn], fac[maxn], inv[maxn], f[maxn], ans, cnt;
    Edge edge[maxn << 1];
     
    inline int C(int x, int y) {
        if (x < y) return 0;
        return (ll)fac[x] * inv[y] % mod * inv[x - y] % mod;
    }
     
    inline void AddEdge(int u, int v) {
        edge[cnt] = (Edge){v, first[u]}, first[u] = cnt++;
        edge[cnt] = (Edge){u, first[v]}, first[v] = cnt++;
    }
     
    void Dfs1(int u, int ff) {
        int e, v;
        size[u] = f[u] = 1;
        for (e = first[u]; ~e; e = edge[e].next)
            if ((v = edge[e].to) ^ ff) {
                Dfs1(v, u);
                f[u] = (ll)f[u] * C(size[u] + size[v] - 1, size[v]) % mod * f[v] % mod;
                size[u] += size[v];
            }
    }
     
    void Dfs2(int u, int ff) {
        int e, v;
        Inc(ans, f[u]);
        for (e = first[u]; ~e; e = edge[e].next)
            if ((v = edge[e].to) ^ ff) {
                f[u] = (ll)f[u] * Pow((ll)C(size[u] - 1, size[v]) * f[v] % mod, mod - 2) % mod;
                size[u] -= size[v], size[v] += size[u];
                f[v] = (ll)f[v] * C(size[v] - 1, size[u]) % mod * f[u] % mod;
                Dfs2(v, u);
                f[v] = (ll)f[v] * Pow((ll)C(size[v] - 1, size[u]) * f[u] % mod, mod - 2) % mod;
                size[v] -= size[u], size[u] += size[v];
                f[u] = (ll)f[u] * C(size[u] - 1, size[v]) % mod * f[v] % mod;
            }
    }
     
    int main() {
        int i, u, v;
        memset(first, -1, sizeof(first));
        scanf("%d", &n);
        for (i = 1; i < n; ++i) scanf("%d%d", &u, &v), AddEdge(u, v);
        fac[0] = fac[1] = inv[0] = inv[1] = 1;
        for (i = 2; i <= n; ++i) inv[i] = (ll)inv[mod % i] * (mod - mod / i) % mod;
        for (i = 2; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % mod, inv[i] = (ll)inv[i] * inv[i - 1] % mod;
        Dfs1(1, 0), Dfs2(1, 0);
        printf("%d
    ", ans);
        return 0;
    }
    

    T4

    枚举最终的牌和第一种的数目

    [sum_{i=0}^{S}sum_{j=l}^{r}inom{n+j-1}{j}inom{m+i-j-1}{i-j} ]

    运用交换求和顺序以及组合恒等式可以得到

    [sum_{i=l}^{r}inom{n+i-1}{n-1}inom{S-i+m}{m} ]

    考虑求

    [sum_{i=0}^{x}inom{n+i-1}{n-1}inom{S-i+m}{m} ]

    可以看成是枚举一个点 ((i,n-1),iin [0,x]) 作为中转站,从 ((0,0))((i,n-1)) 再到 ((i,n)) 再到 ((S,n+m)) 的格路问题。
    也就是 ((0,0))((S,n+m)) 必须经过端点为 ((0,n),(x,n)) 的线段。
    这个可以用总方案数减去经过 ((x,i))((x+1,i)) 之间的边的方案数,其中 (iin[1,n-1])
    注意这里是枚举 (n),所以可以直接算。

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    const int maxn(2e7 + 5);
    const int mod(998244353);
     
    inline void Inc(int &x, const int y) {   x = x + y >= mod ? x + y - mod : x + y;  }
     
    inline void Dec(int &x, const int y) {  x = x - y < 0 ? x - y + mod : x - y;  }
     
    inline int Add(const int x, const int y) {  return x + y >= mod ? x + y - mod : x + y;  }
     
    inline int Sub(const int x, const int y) {  return x - y < 0 ? x - y + mod : x - y;  }
     
    inline int Pow(ll x, int y) {
        ll ret = 1;
        for (; y; y >>= 1, x = x * x % mod)
            if (y & 1) ret = ret * x % mod;
        return ret;
    }
     
    int n, m, s, l, r, f[maxn], g[maxn], inv[maxn];
     
    inline int Calc(int x) {
        int i, ret = 0, y = s - x - 1;
        if (y < 0) return 0;
        f[0] = g[0] = 1;
        for (i = 1; i < n; ++i) f[i] = (ll)f[i - 1] * inv[i] % mod * (x + i) % mod;
        for (i = 1; i <= m + n; ++i) g[i] = (ll)g[i - 1] * inv[i] % mod * (y + i) % mod;
        for (i = 0; i < n; ++i) Dec(ret, (ll)f[i] * g[m + n - i] % mod);
        return ret;
    }
     
    int main() {
        int i;
        scanf("%d%d%d%d%d", &n, &m, &s, &l, &r);
        inv[0] = inv[1] = 1;
        for (i = 2; i <= n + m; ++i) inv[i] = (ll)inv[mod % i] * (mod - mod / i) % mod;
        printf("%d
    ", Sub(Calc(r), Calc(l - 1)));
        return 0;
    }
    

    T5

    实际就是仙人掌上的所有路径的总长度对 (Q) 取模的值。
    建立圆方树,树 (dp),处理出每个点子树内到它的路径数目。
    树边贡献直接算,环边贡献都是一样的,可以考虑枚举端点,也很好算。
    两遍树 (dp) 即可。

    主要代码:头文件太长了

    int mod;
    
    inline void inc(int &x, const int y) {  x = x + y >= mod ? x + y - mod : x + y;  }
    
    inline void dec(int &x, const int y) {  x = x - y < 0 ? x - y + mod : x - y;  }
    
    inline int add(const int x, const int y) {  return x + y >= mod ? x + y - mod : x + y;  }
    
    inline int sub(const int x, const int y) {  return x - y < 0 ? x - y + mod : x - y;  }
    
    inline int fpow(ll x, int y) {
    	ll ret = 1;
    	for (; y; y >>= 1, x = x * x % mod)
    		if (y & 1) ret = ret * x % mod;
    	return ret;
    }
    
    const int maxn(2e5 + 5);
    
    int n, m, tot, dfn[maxn], low[maxn], idx, sta[maxn], top, ans, bel[maxn];
    int sum[maxn], fa[maxn], deep[maxn], type[maxn], cir[maxn], f[maxn];
    vii graph[maxn];
    vi tree[maxn];
    
    inline void addgraph(int u, int v, int w) {  graph[u].pb(mp(v, w)), graph[v].pb(mp(u, w));  }
    
    inline void addtree(int u, int v) {  tree[u].pb(v), tree[v].pb(u);  }
    
    void dfs(int u) {
    	int v, s;
    	dfn[u] = low[u] = ++idx, sta[++top] = u;
    	for (auto cur : graph[u])
    		if (!dfn[v = cur.first]) {
    			fa[v] = u, deep[v] = add(deep[u], cur.second);
    			dfs(v), cmin(low[u], low[v]);
    			if (low[v] >= dfn[u]) {
    				++tot, s = 0;
    				do {
    					++s, addtree(tot, sta[top--]);
    					bel[sta[top + 1]] = tot;
    				} while (sta[top + 1] ^ v);
    				addtree(tot, u);
    				if (s == 1) type[tot] = 1, sum[tot] = cur.second;
    			}
    		}
    		else {
    			cmin(low[u], dfn[v]);
    			if (low[v] == dfn[u]) sum[bel[v]] = add(cur.second, sub(deep[v], deep[u]));
    		}
    }
    
    void dfs1(int u, int ff) {
    	int val = type[u] ? 1 : 2;
    	f[u] = u <= n;
    	for (int v : tree[u]) if (v ^ ff) dfs1(v, u), inc(f[u], (ll)f[v] * val % mod);
    }
    
    void dfs2(int u, int ff) {
    	int val = type[u] ? 1 : 2, ret = 0, s = 0;
    	if (u > n) {
    		for (int v : tree[u]) inc(s, (ll)ret * f[v] % mod), inc(ret, f[v]);
    		inc(ans, (ll)s * sum[u] % mod);
    	}
    	for (int v : tree[u])
    		if (v ^ ff) {
    			dec(f[u], (ll)f[v] * val % mod);
    			inc(f[v], (ll)f[u] * (type[v] ? 1 : 2) % mod);
    			dfs2(v, u);
    			dec(f[v], (ll)f[u] * (type[v] ? 1 : 2) % mod);
    			inc(f[u], (ll)f[v] * val % mod);
    		}
    }
    
    int main() {
    	int i, u, v, w;
    	read(n, m, mod), tot = n;
    	for (i = 1; i <= n; ++i) type[i] = 1;
    	for (i = 1; i <= m; ++i) read(u, v, w), addgraph(u, v, w);
    	dfs(1), dfs1(1, 0), dfs2(1, 0), print(ans);
    	return 0;
    }
    

    T6

    如果我们能够构造一个多项式 (H(x)=prod_{i有二次剩余}(x−i)^k),那么再将该多项式与 (F(x))(Gcd) 可以得到答案多项式。
    二次剩余有一个判断式 (x^{frac{p−1}{2}}−1equiv 0(mod~p))
    所以 (x^{frac{p−1}{2}}−1=prod_{i有二次剩余}(x-i)(mod~p))
    只需要先将 ((x^{frac{p−1}{2}}−1)^k)(F(x)) 取模,接下来两个多项式的次数就都是 (O(n)) 级别的。
    可以利用快速幂算出 (x^{frac{p−1}{2}})((x^{frac{p−1}{2}}−1)^k)

    主要代码:

    Poly Gcd(Poly x, Poly y) {
        if (!y.size()) return x;
        Poly z = x % y;
        while (z.size() && !z[z.size() - 1]) z.pop_back();
        return Gcd(y, z);
    }
     
    int n, k;
    Poly f, g, pw;
     
    int main() {
        int i, x, inv;
        scanf("%d%d", &n, &k), f.resize(n + 1);
        for (i = 0; i <= n; ++i) scanf("%d", &f[i]);
        g.resize(2), g[1] = 1, pw.resize(1), pw[0] = 1;
        for (x = (mod - 1) >> 1; x; g = g * g % f, x >>= 1)
            if (x & 1) pw = pw * g % f;
        Dec(pw[0], 1), g.resize(1), g[0] = 1;
        for (x = k; x; pw = pw * pw % f, x >>= 1)
            if (x & 1) g = g * pw % f;
        g = Gcd(f, g);
        printf("%d
    ", x = (int)g.size() - 1);
        inv = Pow(g[x], mod - 2), g = g * inv;
        for (i = 0; i <= x; ++i) printf("%d ", g[i]);
        return puts(""), 0;
    }
    
  • 相关阅读:
    大道至简观后感
    冲刺第二天
    梦断代码阅读笔记 02
    冲刺第一天
    第十周学习进度
    个人冲刺第一阶段个人任务--界面
    软工第二周个人作业
    软件工程开课博客(自我介绍)
    梦断代码阅读笔记01
    第二周学习进度报告
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/10596121.html
Copyright © 2020-2023  润新知