• 电路图题解


    OJ 1177 1178 1179

    一.电路图A

    第一问,容易看出$右拐次数=左拐次数+4$,$左拐+右拐=n$,所以$右拐=n/2-2$,相当于$C_{n}^{n/2-2}$

    第二问,总个数除去最左端,最右端,最上方,最下方的电阻,整个电路被分成$4$段,每一段都有偶数个电阻,答案就是将$n$分成$4$个偶数的情况,相当于将$n/2$分成任意数的情况,用隔板法,答案就是$C_{n/2-1}^{3}$,排除旋转的影响,答案还要除以$4$。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <deque>
    #include <string>
    using namespace std;
    
    inline long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(!isdigit(ch))
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
        while(isdigit(ch));
        return ans * f;
    }
    
    const int MOD = 1e9+7, MAXN = 1e7 + 1;
    int fac[MAXN], inv[MAXN];
    
    int pow(int a,int p){
        int ans = 1;
        while(p){
            if(p & 1) ans = (long long)ans * a % MOD;
            a = (long long)a * a % MOD, p >>= 1;
        }
        return ans;
    }
    
    inline int C(int n,int m){
        return (long long)fac[n] * inv[m] % MOD * inv[n-m] % MOD;
    }
    
    int main(){
        
        int n = read(), ans1, ans2;
        fac[0] = 1;
        for(int i=1; i<=n; i++)
            fac[i] = (long long)fac[i-1] * i % MOD;
        inv[n] = pow(fac[n], MOD-2);
        for(int i=n-1; i>=0; i--)
            inv[i] = (long long)inv[i+1] * (i+1) % MOD;
        ans1 = C(n, n/2-2);
        ans2 = (long long)C(n/2+1, 3) * n % MOD * pow(4, MOD-2) % MOD;
        printf("%d
    %d", ans1, ans2);
        return 0;
    }
    View Code

    二.电路图B

    区间修改,区间查询,一看就是线段树,关键在于合并标记如何合并。

    可将一个标记记为$frac{ax+b}{cx+d}$,用四个数表示为${a,b,c,d}$,串联一个电阻就是${0,R,0,1}$,并联一个电阻就是${R,0,1,R}$

    最关键是mix操作,将${a,b,c,d}$和${i,j,k,p}$合成为${ai+cj,bi+dj,ak+cp,bk+dp}$,最终就转化为线段树加标记的问题了。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    using namespace std;
    
    long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar();
        while(ch >= '0' && ch <= '9');
        return ans * f;
    }
    
    const int MAXN = 250001, MAXM = 250001;
    
    struct sign{double a, b, c, d;};
    
    struct Segment{
        int l, r;
        double maxNum, minNum;
        sign add;
        bool isSigned;
    }seg[MAXN*4];
    int a[MAXN];
    
    void change(int x,sign fa){
        sign t = seg[x].add;
        seg[x].isSigned = true;
        double a, b, c, d;
        a = t.a * fa.a + t.c * fa.b;
        b = t.b * fa.a + t.d * fa.b;
        c = t.a * fa.c + t.c * fa.d;
        d = t.b * fa.c + t.d * fa.d;
        seg[x].add = (sign){1, b/a, c/a, d/a};
        seg[x].maxNum = (seg[x].maxNum * fa.a + fa.b) / (seg[x].maxNum * fa.c + fa.d);
        seg[x].minNum = (seg[x].minNum * fa.a + fa.b) / (seg[x].minNum * fa.c + fa.d);
    }
    
    void spread(int x){
        if(seg[x].isSigned){
            change(x*2, seg[x].add);
            change(x*2+1, seg[x].add);
            seg[x].isSigned = false;
            seg[x].add = (sign){1, 0, 0, 1};
        }
    }
    
    void build(int l,int r,int x=1){
        seg[x].l = l, seg[x].r = r;
        seg[x].add = (sign){1, 0, 0, 1};
        if(l == r){
            seg[x].maxNum = seg[x].minNum = a[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, x*2);
        build(mid+1, r, x*2+1);
        seg[x].maxNum = max(seg[x*2].maxNum, seg[x*2+1].maxNum);
        seg[x].minNum = min(seg[x*2].minNum, seg[x*2+1].minNum);
    }
    
    void point(int l,int r,sign vis,int x=1){
        if(l <= seg[x].l && seg[x].r <= r){
            change(x, vis);
            return;
        }
        spread(x);
        if(seg[x*2].r >= l) point(l, r, vis, x*2);
        if(seg[x*2+1].l <= r) point(l, r, vis, x*2+1);
        seg[x].maxNum = max(seg[x*2].maxNum, seg[x*2+1].maxNum);
        seg[x].minNum = min(seg[x*2].minNum, seg[x*2+1].minNum);
    }
    
    double ask(int l,int r,int type,int x=1){
        if(l <= seg[x].l && seg[x].r <= r)
            return (type == 1) ? seg[x].maxNum : seg[x].minNum;
        spread(x);
        double ans = (type == 1) ? 0 : 1e9;
        if(seg[x*2].r >= l) (type == 1) ? ans = max(ans, ask(l, r, type, x*2)) : ans = min(ans, ask(l, r, type, x*2));
        if(seg[x*2+1].l <= r) (type == 1) ? ans = max(ans, ask(l, r, type, x*2+1)) : ans = min(ans, ask(l, r, type, x*2+1));
        return ans;
    }
    
    int main(){
        int n = read();
        for(int i=1; i<=n; i++)
            a[i] = read();
        build(1, n);
        int m = read();
        for(int i=1; i<=m; i++){
            int op = read(), l = read(), r = read();
            if(op == 1) printf("%.10lf
    ", ask(l, r, 1));
            else if(op == 2) printf("%.10lf
    ", ask(l, r, 2));
            else{
                int R = read();
                if(op == 3) point(l, r, (sign){1, R, 0, 1});
                else point(l, r, (sign){R, 0, 1, R});
            }
        }
        return 0;
    }
    View Code

    三.电路图C:(全网首发)

    大概题意:给你一张图,用k种颜色给图染色,保证相邻两个顶点不染同一种颜色,求染色方案数的表达式。

    乍一看这道题可能无从下手,但经过简单的计算(可以试试数学课上排列组合的题目)发现最特别的情况就是几个点涂相同颜色的情况,那么就从全集中枚举涂相同颜色的集合,若最后枚举出来有$p$个集合,就会对答案产生$k(k-1)(k-2)……(k-p+1)$的贡献($p$个集合之间颜色互不相同),当然在枚举集合的时候要判断集合之间的点是否有连边。

    这样,你就得到了$40$分!

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar();
        while(ch >= '0' && ch <= '9');
        return ans * f;
    }
    
    const int SIZE = 18;
    struct poly{
        int conf[SIZE+1];
        poly(){memset(conf, 0, sizeof(conf));}
        poly operator * (poly x){
            poly ans;
            for(int i=0; i<=SIZE; i++)
                for(int j=0; j<=SIZE-i; j++)
                    ans.conf[i+j] += conf[i] * x.conf[j];
            return ans;
        }
        poly operator + (poly x){
            poly ans;
            for(int i=0; i<=SIZE; i++)
                ans.conf[i] = conf[i] + x.conf[i];
            return ans;
        }
    };
    
    const int MAXN = 19;
    poly fac[MAXN], ans;
    int g[MAXN][MAXN], n, m;
    
    bool judge(int vis){
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                if((vis & (1<<i)) && (vis & (1<<j)) && g[i][j])
                    return false;
        return true;
    }
    
    void dfs(int vis,int deep){
        if(!vis){
            ans = ans + fac[deep];
            return;
        }
        int x = vis & (-vis);
        vis ^= x;
        for(int s=vis; ; s=(s-1)&vis){
            if(judge(s^x))
                dfs(vis^s, deep+1);
            if(!s)
                break;
        }
    }
    
    int main(){
        n = read(), m = read();
        for(int i=1; i<=n; i++){
            fac[i].conf[1] = 1;
            fac[i].conf[0] = 1-i;
            if(i != 1) fac[i] = fac[i-1] * fac[i];
        }
        for(int i=1; i<=m; i++){
            int x = read(), y = read();
            g[x][y] = g[y][x] = true;
        }
        dfs((1<<n)-1, 0);
        int len = SIZE;
        while(ans.conf[len] == 0)
            len--;
        for(int i=0; i<=len; i++)
            printf("%d
    ", ans.conf[i]);
        return 0;
    }
    View Code

    因为一看数据规模就是成倍增长的,也就是说如果你优化了$O(n)$的复杂度你就可以多过3,4个点,首先当你枚举完时不要马上加到$ans$中,其次用状态压缩辅助判断集合是否合法。

    期望得分:100,实际得分:70

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar();
        while(ch >= '0' && ch <= '9');
        return ans * f;
    }
    
    const int SIZE = 18;
    struct poly{
        int conf[SIZE+1];
        poly(){memset(conf, 0, sizeof(conf));}
        poly operator * (poly x){
            poly ans;
            for(int i=0; i<=SIZE; i++)
                for(int j=0; j<=SIZE-i; j++)
                    ans.conf[i+j] += conf[i] * x.conf[j];
            return ans;
        }
        poly operator + (poly x){
            poly ans;
            for(int i=0; i<=SIZE; i++)
                ans.conf[i] = conf[i] + x.conf[i];
            return ans;
        }
    };
    
    const int MAXN = 19;
    poly fac[MAXN], ans;
    int g[MAXN], n, m, cnt[MAXN];
    int valid[1<<(MAXN-1)];
    
    bool judge(int vis){
        if(valid[vis] != -1)
            return valid[vis];
        for(int i=0; i<n; i++)
            if((vis & (1<<i)) && (vis & g[i]))
                return valid[vis] = false;
        return valid[vis] = true;
    }
    
    void dfs(int vis,int deep){
        if(!vis){cnt[deep]++; return;}
        int x = vis & (-vis);
        vis ^= x;
        for(int s=vis; ; s=(s-1)&vis){
            if(judge(s^x))
                dfs(vis^s, deep+1);
            if(!s)
                break;
        }
    }
    
    int main(){
        memset(valid, -1, sizeof(valid));
        n = read(), m = read();
        for(int i=1; i<=n; i++){
            fac[i].conf[1] = 1;
            fac[i].conf[0] = 1-i;
            if(i != 1) fac[i] = fac[i-1] * fac[i];
        }
        for(int i=1; i<=m; i++){
            int x = read(), y = read();
            g[x] |= (1<<y), g[y] |= (1<<x);
        }
        dfs((1<<n)-1, 0);
        poly t;
        for(int i=1; i<=n; i++){
            t.conf[0] = cnt[i];
            ans = ans + t * fac[i];
        }
        int len = SIZE;
        while(ans.conf[len] == 0)
            len--;
        for(int i=0; i<=len; i++)
            printf("%d
    ", ans.conf[i]);
        return 0;
    }
    View Code

    为什么呢,我发现我时间复杂度的估计有误,因为在$dfs$的时候枚举到了重复的$vis$,导致最后答案并不是成倍增长。其次$dfs$其实也有重复,对于一个$vis$,可以将接下来的搜索对答案的贡献保存下来(想象$dfs(vis,1)$和$dfs(vis,2)$之间贡献相差了多少呢?其实就是一个$k$而已),所以可以用记忆化搜索,将vis对答案的贡献用多项式保存下来,转移的时候只需把系数都左移一位(十进制的)即可。

    时间复杂度:$O(nB_n)->O(n2^n)$,$B_n$是贝尔数,就是斯特林数的一层的和,如果不用$dp$那么一个图理论能分成多少个集合时间复杂度就是多少。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar();
        while(ch >= '0' && ch <= '9');
        return ans * f;
    }
    
    const int SIZE = 18;
    struct poly{
        int conf[SIZE+1];
        poly(){memset(conf, 0, sizeof(conf));}
        poly operator * (poly x){
            poly ans;
            for(int i=0; i<=SIZE; i++)
                for(int j=0; j<=SIZE-i; j++)
                    ans.conf[i+j] += conf[i] * x.conf[j];
            return ans;
        }
        poly operator + (poly x){
            poly ans;
            for(int i=0; i<=SIZE; i++)
                ans.conf[i] = conf[i] + x.conf[i];
            return ans;
        }
    };
    
    const int MAXN = 19;
    poly fac[MAXN], ans, dp[1<<(MAXN-1)];
    int g[MAXN], n, m;
    int valid[1<<(MAXN-1)], isCount[1<<(MAXN-1)];
    
    bool judge(int vis){
        if(valid[vis] != -1)
            return valid[vis];
        for(int i=0; i<n; i++)
            if((vis & (1<<i)) && (vis & g[i]))
                return valid[vis] = false;
        return valid[vis] = true;
    }
    
    poly dfs(int vis){
        if(isCount[vis]) return dp[vis];
        int x = vis & (-vis), rest = vis ^ x;
        poly cnt;
        for(int s=rest; ; s=(s-1)&rest){
            if(judge(s^x))
                cnt = cnt + dfs(rest^s);
            if(!s) break;
        }
        for(int i=SIZE; i>=1; i--)
            cnt.conf[i] = cnt.conf[i-1];
        cnt.conf[0] = 0, isCount[vis] = true;
        return dp[vis] = cnt;
    }
    
    int main(){
        memset(valid, -1, sizeof(valid));
        n = read(), m = read();
        for(int i=1; i<=n; i++){
            fac[i].conf[1] = 1;
            fac[i].conf[0] = 1-i;
            if(i != 1) fac[i] = fac[i-1] * fac[i];
        }
        for(int i=1; i<=m; i++){
            int x = read(), y = read();
            g[x] |= (1<<y), g[y] |= (1<<x);
        }
        poly t, cnt;
        isCount[0] = true, dp[0].conf[0] = 1;
        cnt = dfs((1<<n)-1);
        for(int i=1; i<=n; i++){
            t.conf[0] = cnt.conf[i];
            ans = ans + t * fac[i];
        }
        int len = SIZE;
        while(ans.conf[len] == 0)
            len--;
        for(int i=0; i<=len; i++)
            printf("%d
    ", ans.conf[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    OC面向对象—封装
    设计模式之类关系
    理性:中国别一厢情愿救俄罗斯(转)
    Mockito--完整功能介绍(转)
    从陌陌上市看BAT的移动保卫战(转)
    This exception may occur if matchers are combined with raw values
    RepositoryClassLoader.java
    搭建你的持续集成server
    mysql中怎样查看和删除唯一索引
    Android中Context具体解释 ---- 你所不知道的Context
  • 原文地址:https://www.cnblogs.com/PHDHD/p/12276110.html
Copyright © 2020-2023  润新知