• ZROI week3


    作业

    poj 1091 跳蚤

    容斥原理。

    考虑能否跳到旁边就是卡牌的\(gcd\)是否是1,可以根据裴蜀定理证明。

    考虑正着做十分的麻烦,所以倒着做,也就是用\(M^N - (不合法)\)即可。

    不合法显然就是\(gcd\)不为1的情况,那么我们考虑枚举\(gcd\)\((1 \leq gcd \leq 15)\),第\(n + 1\)个数一直是\(m\)所以不用理它。

    考虑每个\(gcd\)的贡献,如果所有的都能被整除,那么产生的方案数就是:

    \(({m \over gcd})^n\)

    表示在小于等于\(m\)的数中有多少能够被当前的\(gcd\)整除,那么每个位置就有这么多数字可以选择,就转化成了容斥原理的问题了,\(dfs\)枚举\(m\)的质因数统计即可。

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    #define int long long
    int n,m;
    const int MAXN = 1e5 + 10;
    int Ans;
    int p[MAXN];
    int ans;
    int div (int now) {
    	int res = 0;
    	for(int i = 2;i * i <= now; ++i) {
    		if(now % i == 0) {
    			p[++res] = i;
    			now /= i;
    			while(now % i == 0) now /= i;
    		}
    	}
    	if(now > 1) p[++res] = now;
    	return res;
    }
    
    
    int pow_mod(int a,int b) {
    	int res = 1;
    	while(b) {
    		if(b & 1) res = res * a;
    		a = a * a;
    		b >>= 1;
    	}
    	return res;
    }
    
    void dfs(int num,int y,int now) {
    	if(now > m) return;
    	if(num == ans + 1) {
    		if(y) {
    			if(y & 1) {
    				Ans -= pow_mod(m / now,n);
    			}
    			else {
    				Ans += pow_mod(m / now,n);
    			}
    		}
    		return;
    	}
    	dfs(num + 1,y,now);
    	dfs(num + 1,y + 1,now * p[num]);
    }
    
    signed main () {
    	cin >> n >> m;
    	Ans = pow_mod(m,n);
    	//cout<<Ans<<endl;
    	ans = div(m);
    	//cout<<ans<<endl;
    	dfs(1,0,1);
    	cout<<Ans<<endl;
    	return 0;
    }
    

    ps:这题为啥不用取模?真是让人难以琢磨。

    hdu 5121 just a mistake

    期望\(dp\)+容斥原理

    题目大意就是让你随机构造一个排列,然后判断是否与集合中的点有连边然后一个一个的连接。

    乍一看十分难做,第二眼还是十分难做,做了\(1h23min\)。。。

    首先考虑如何统计贡献,一般的方程就是如果第\(i\)\(p_i\)的时候,某些贡献是多少,由于这个问题拓展到了树上,那么就是设\(f_{i,j}\)表示以\(i\)为根的子树中,\(i\)排在第\(j\)位且必须选的方案数。

    那么子树中的点如果排在\(i\) 的前面就不能选,因为\(i\)必选,贡献就得减去它。

    如果在后面,选了\(i\)之后也不能选它,然后组合数统计答案即可。

    具体就是以每个点为根去做搜索,统计出答案之后相加即可,由于期望的线性性。

    再来说说具体怎么计算答案,枚举\(y\)这棵子树的1到siz[y]的集合中前\(k\)个插入到\(i\)前面,后面的插在后面,乘上\(f_{i,j}\),在乘上选位置的方案数。

    选位置的方案数就是\(C_{n + m}^{n}\)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define int long long
    const int MAXN = 210;
    int C[MAXN][MAXN];
    int siz[MAXN];
    int f[MAXN][MAXN];
    int lnk[MAXN];
    int fac[MAXN];
    const int mod = 1e9 + 7;
    int cnt;
    int head[MAXN << 1];
    void pre() {
    	memset(C,0,sizeof C);
    	memset(fac,0,sizeof fac);
    	C[0][0] = 1;
    	fac[0] = 1;
    	for(int i = 1;i <= 200; ++i) {
    		C[i][0] = C[i][i] = 1;
    		fac[i] = (fac[i - 1] * i) % mod;
    		for(int j = 1;j <= i; ++j) {
    			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
    		}
    	}
    	return;
    }
    int T,n,x,y;
    int ans;
    struct edge {
    	int to;
    	int nxt;
    }e[MAXN << 1];
    void add(int u,int v) {
    	e[++cnt].to = v;
    	e[cnt].nxt = head[u];
    	head[u] = cnt;
    	return;
    }
    
    void Add(int u,int v) {
    	add(u,v);
    	add(v,u);
    }
    
    void dfs(int x,int fa) {
    	f[x][0] = 0;f[x][1] = 1;
    	siz[x] = 1;
    	for(int i = head[x];i;i=e[i].nxt) {
    		int y = e[i].to;
    		if(y == fa) continue;
    		dfs(y,x);
    		int all_state = fac[siz[y]];
    		int tmp = siz[y];
    		int sum_state = siz[x] + siz[y];
    		siz[x] += siz[y];
    		memset(lnk,0,sizeof lnk);
    		for(int j = 0;j <= siz[y]; ++j,--tmp) {
    			all_state = (all_state + mod - f[y][j]) % mod;
    			for(int k = 1;k <= sum_state - siz[y]; ++k) {
    				lnk[j + k] = ((lnk[j + k] + all_state * f[x][k] % mod * C[j + k - 1][j] % mod * C[sum_state - j - k][tmp] % mod) % mod + mod) % mod;
    			}
    		}
    		for(int j = 0;j <= sum_state; ++j) {
    			f[x][j] = lnk[j];
    		}
    	}
    }
    int cas;
    signed main () {
    	scanf("%lld",&T);
    	pre();
    	while(T--) {
    		scanf("%lld",&n);
    		memset(head,0,sizeof head);
    		cnt = 0;
    		memset(f,0,sizeof f);
    		memset(lnk,0,sizeof lnk);
    		memset(siz,0,sizeof siz);
    		for(int i = 1;i < n; ++i) {
    			scanf("%lld %lld",&x,&y);
    			Add(x,y);
    		}
    		ans = 0;
    		for(int i = 1;i <= n; ++i) {
    			memset(f,0,sizeof f);
    			dfs(i,0);
    			for(int j = 1;j <= n; ++j) {
    				ans = (ans + f[i][j]) % mod;
    			}
    		}
    		printf("Case #%lld: %lld\n",++cas,ans);
    	}
    	return 0;
    }
    

    ps:组合数没取模调了半天没看出来。。。

    hdu 4336 Card Collector

    裸.容斥原理

    一坨式子懒得写了.

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    #define db double
    int n,tmp;
    db p[21];
    db ans;
    db sum;
    void dfs(int now,db P,int st) {
    	if(now == tmp) {
    		ans += 1.0 / P;
    		return;
    	}
    	for(int i = st;i <= n; ++i) {
    		dfs(now + 1,P + p[i],i + 1);
    	}
    }
    int main () {
    	while(~scanf("%d",&n)) {
    		sum = 0;
    		for(int i = 1;i <= n; ++i) {
    			scanf("%lf",&p[i]);
    			sum += 1.0 / p[i];
    		}
    		for(int i = 2;i <= n; ++i) {
    			tmp = i;
    			ans = 0;
    			dfs(0,0,1);
    			//cout<<ans<<endl;
    			if((i & 1) == 0) {
    				ans = -ans;
    			}
    			sum += ans;
    		}
    		printf("%.6lf\n",sum);
    	}
    	return 0;
    }
    

    hdu 6314

    容斥原理。

    挖挫...这题真的是卡常数??

    首先至少这种问题在高中就学过用补集去算,设\(f_{n,m}\)表示没有任何一行一列有全是黑色。

    简单推一下这个就是:

    \(f_{n,m} = \sum_{i = 0}^{n} \sum_{j = 0}^{m} C_{n}^{i} \times C_{m}^{j} \times (-1)^{i + j} \times 2^{(n - i) \times (m - j)}\)

    答案首先就是:

    \(Ans = \sum_{i = 0}^{n - A} \sum_{j = 0}^{m - B} C_{n}^{i} \times C_{m}^{j} \times f_{i,j}\)

    \(\;\;\;\;\;\;\;= \sum_{i = 0}^{n - A} \sum_{j = 0}^{m - B} C_{n}^{i} \times C_{m}^{j} \sum_{u = 0}^{i} \sum_{v = 0}^{j} C_{i}^{u} \times C_{j}^{v} \times (-1)^{u + v} \times 2^{(i - u) \times (j - v)}\)

    考虑哪些是可以直接处理出来的,例如这个:

    \(\sum \sum C_{n}^{i} \times C_{m}^{j}\)

    就可以直接算一手...

    考虑后面的怎么算?

    组合数展开就行了,预处理出形如\(i + j = k\)并且\(i\)的值大于等于\(l\) 的和,然后处理\(g_{i,j} = {2^{i \times j} \over i! \times j!}\)

    枚举\(u,v\)即可。

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    using namespace std;
    #define int long long
    const int mod = 998244353;
    const int MAXN = 3010;
    int C[MAXN][MAXN];
    int g[MAXN][MAXN];
    int fac[MAXN];
    int inv[MAXN];
    int n,m,A,B;int ans;
    int two[MAXN * MAXN];
    int pow_mod(int a,int b) {
        int res = 1;
        while(b) {
            if(b & 1) res = res * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    }
    void pre() {
        fac[0] = 1;
        inv[0] = 1;
        two[0] = 1;
        for(int i = 1;i <= MAXN * MAXN; ++i) {
            two[i] = two[i - 1] * 2 % mod;
        }
        for(int i = 1;i <= MAXN; ++i) {
            fac[i] = fac[i - 1] * i % mod;
            inv[i] = pow_mod(fac[i],mod - 2);
        }
        for(int i = 0;i <= 3000; ++i) {
            for(int j = i;j >= 0; --j) {
                C[i][j] = (((i - j) & 1 ? (mod - (inv[j] * inv[i - j] % mod)) : (inv[j] * inv[i - j] % mod)) + C[i][j + 1]) % mod;
                //cout<<C[i][j]<<endl;
            }
        }
        for(int i = 0;i <= 3000; ++i) {
            for(int j = 0;j <= 3000; ++j) {
                g[i][j] = two[i * j] * inv[i] % mod * inv[j] % mod;
            //if(j <= 30 and i == 0) cout<<g[i][j]<<endl;
            }
        }
    }
    int cnt = 0;
    //i == 1 and j == 1
    signed main () {
        pre();
        while(~scanf("%d %d %d %d",&n,&m,&A,&B)) {
            ans = 0;
            for(int i = 0;i <= n - A; ++i) {
                for(int j = 0;j <= m - B; ++j) {
                    ans = ((ans + C[n - i][A] * C[m - j][B] % mod * g[i][j] % mod) % mod + mod) % mod;
                    //cout<<ans<<endl;
                }
            }
            //n : 3 m : 4 A : 1 B : 2
            //cout<<g[1][1]<<endl;
            //cout<<two[1]<<' '<<inv[1]<<' '<<inv[1]<<endl;
            //cout<<C[2][1]<<' '<<C[3][2]<<endl;
            ans = ans * fac[n] % mod * fac[m] % mod;
            ans = (ans % mod + mod) % mod;
            cout<<ans<<endl;
        }
        return 0;
    }
    			
    

    ps:处理\(two\)数组的时候我居然乘的是2...

    但是...\(AC\)不了...

    卡常!

    卡了三点常就过了:

    • 去掉#define int long long改为1ll * ...去算
    • int变为register int
    • 函数前面加上inline

    然后呢?就过了。。。

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    using namespace std;
    #define rint register int
    const int mod = 998244353;
    const int MAXN = 3010;
    int C[MAXN][MAXN];
    int g[MAXN][MAXN];
    int fac[MAXN];
    int inv[MAXN];
    int n,m,A,B;int ans;
    int two[MAXN * MAXN];
    inline int pow_mod(int a,int b) {
    	int res = 1;
    	while(b) {
    		if(b & 1) res = 1ll * res * a % mod;
    		a = 1ll * a * a % mod;
    		b >>= 1;
    	}
    	return res;
    }
    inline void pre() {
    	fac[0] = 1;
    	inv[0] = 1;
    	two[0] = 1;
    	for(rint i = 1;i <= MAXN * MAXN; ++i) {
    		two[i] = two[i - 1] * 2 % mod;
    	}
    	for(rint i = 1;i <= MAXN; ++i) {
    		fac[i] = 1ll * fac[i - 1] * i % mod;
    		inv[i] = pow_mod(fac[i],mod - 2);
    	}
    	for(rint i = 0;i <= 3000; ++i) {
    		for(rint j = i;j >= 0; --j) {
    			C[i][j] = (((i - j) & 1 ? (mod - (1ll * inv[j] * inv[i - j] % mod)) : (1ll * inv[j] * inv[i - j] % mod)) + C[i][j + 1]) % mod;
    			//cout<<C[i][j]<<endl;
    		}
    	}
    	for(rint i = 0;i <= 3000; ++i) {
    		for(rint j = 0;j <= 3000; ++j) {
    			g[i][j] = 1ll * two[i * j] * inv[i] % mod * inv[j] % mod;
    		//if(j <= 30 and i == 0) cout<<g[i][j]<<endl;
    		}
    	}
    }
    int cnt = 0;
    //i == 1 and j == 1
    signed main () {
    	pre();
    	while(~scanf("%d %d %d %d",&n,&m,&A,&B)) {
    		ans = 0;
    		for(rint i = 0;i <= n - A; ++i) {
    			for(rint j = 0;j <= m - B; ++j) {
    				ans = ((ans + 1ll * C[n - i][A] * C[m - j][B] % mod * 1ll * g[i][j] % mod) % mod + mod) % mod;
    				//cout<<ans<<endl;
    			}
    		}
    		//n : 3 m : 4 A : 1 B : 2
    		//cout<<g[1][1]<<endl;
    		//cout<<two[1]<<' '<<inv[1]<<' '<<inv[1]<<endl;
    		//cout<<C[2][1]<<' '<<C[3][2]<<endl;
    		ans = ans * 1ll * fac[n] % mod * fac[m] % mod;
    		ans = (ans % mod + mod) % mod;
    		printf("%d\n",ans);
    	}
    	return 0;
    }
    			
    

    hdu 5838

    状压+容斥

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    //const int mod = 772002;
    const int mod = 12345678;
    int cnt;
    int n,m;
    int cas;
    int ans;
    int dx[8] = {1,-1,0,0,1,-1,1,-1};
    int dy[8] = {0,0,1,-1,1,1,-1,-1};
    int nx[10];
    int ny[10];
    int f[30][1 << 10];
    int sta[1 << 10];
    char s[7][7];
    int vis[7][7];
    
    int ok(int x,int y) {
        return x >= 0 && x < n && y >= 0 && y < m;
    }
    
    int dp() {
        cnt = 0;
        for(int i = 0;i < n; ++i) {
            for(int j = 0;j < m; ++j) {
                if(s[i][j] == 'X') {
                    nx[cnt] = i;
                    ny[cnt++] = j;
                }
            }
        }
        memset(sta,0,sizeof sta);
        for(int i = 0;i < (1 << cnt); ++i) {
            memset(vis,0,sizeof vis);
            for(int j = 0;j < cnt; ++j) {
                if(~i & (1 << j)) {
                    for(int k = 0;k < 8; ++k) {
                        int Nx = nx[j] + dx[k];
                        int Ny = ny[j] + dy[k];
                        if(!ok(Nx,Ny)) continue;
                        vis[Nx][Ny] = 1;
                    }
                    vis[nx[j]][ny[j]] = 1;
                }
            }
            
            for(int j = 0;j < n; ++j) {
                for(int k = 0;k < m; ++k) {
                    if(!vis[j][k]) {
                        sta[i] ++;
                    }
                }
            }
        }
        int all_sta = n * m;
        memset(f,0,sizeof f);
        f[0][0] = 1;
        for(int i = 1; i <= all_sta; ++i) {
            for(int j = 0;j < (1 << cnt); ++j) {
                for(int k = 0;k < cnt; ++k) {
                    if(j & (1 << k)) {
                        f[i][j] = (f[i][j] + f[i - 1][j ^ (1 << k)]) % mod;
                    }
                }
                f[i][j] = ((f[i][j] + 1ll * f[i - 1][j] * max(0ll,1ll * sta[j] - i + 1) % mod) % mod + mod) % mod;
            }
        }
        return f[all_sta][(1 << cnt) - 1];
    }
    
    void dfs(int now,int lim,int f) {
        if(lim == m) {
            ans = ((ans + 1ll * dp() * f % mod) % mod + mod) % mod;
            return;
        }
        else if(now == n) {
            dfs(0,lim + 1,f);
            return;
        }
        else {
            dfs(now + 1,lim,f);
        }
        if(s[now][lim] != 'X') {
            for(int j = 0;j < 8; ++j) {
                int new_x = now + dx[j];
                int new_y = lim + dy[j];
                if(ok(new_x,new_y) && s[new_x][new_y] == 'X') goto state;
            }
            s[now][lim] = 'X';
            dfs(now + 1,lim,-f);
            s[now][lim] = '.';
            state : ;
        }
    }
    signed main () {
        while(~scanf("%lld %lld",&n,&m)) {
            ans = 0;
            for(int i = 0;i < n; ++i) {
                scanf("%s",s[i]);
            }
            dfs(0,0,1);
            //cout<<"Case #"<<(++cas)<<':'<<' '<<(((ans % mod + mod) % mod) == m ? 0 : ((ans % mod + mod) % mod))<<endl;
            cout<<((ans % mod + mod) % mod)<<endl;
        }
        return 0;
    }
    /*
    2 4
    .X..
    ...X
    4 2
    X.
    ..
    ..
    X.
    1 2
    XX
    */
    
    /*
    Case #1: 2100
    Case #2: 2520
    Case #3: 0
    */
    
  • 相关阅读:
    powerful number 小记
    CF573E Bear and Bowling
    Diary 2.0
    【LOJ2540】「PKUWC2018」随机算法
    【Luogu2496】【BZOJ3005】[SDOI2012]体育课
    CF-diary
    【CF1217F】Forced Online Queries Problem
    NOI2019 选做
    Codeforces Round #568 (Div. 2) 选做
    【LOJ2513】「BJOI2018」治疗之雨
  • 原文地址:https://www.cnblogs.com/akoasm/p/10215774.html
Copyright © 2020-2023  润新知