zoukankan      html  css  js  c++  java
  • 1103上午考试

    1103上午考试

    T2

    ​ 题目大意:

    ​ 给定一张(n)个节点(m)条边的有向图,问有多少个子图(一个边集)是没有环的.(n <= 17)

    ​ 状压DP.

    ​ 设(f[i])表示点集为(i)时有多少个无环的子图.

    ​ 我们对一个有向图分层,入度为0的点构成第一层,删去第一层后入度为0的点构成第二层,以此类推.

    ​ 然后我们可以枚举最后一层的点集(j), (i)(j)交集为空,然后我们可以得到(i)(j)之间的连边为(cnt)条,可以得到DP转移方程:

    (f[i | j] = f[i] * 2^{cnt}),因为这(cnt)条边每条边都可以选择加或不加进新的点集中.

    ​ 可是这么写会算重复,因为(i | j)的点集会有很多种组成.所以考虑容斥,新的转移方程就是:

    (f[i| j] = f[i] * 2 ^{cnt} * (-1) ^{size[j] + 1}).

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
    	long long s = 0, f = 1; char ch;
    	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    	for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    	return s * f;
    }
    
    const int N = 17, mod = 1e9 + 7;
    int n, m, ans, U;
    int mp[N + 1][N + 1], d[(1 << N) + 1], siz[(1 << N) + 1], powe[305], f[(1 << N) + 1], sum[(1 << N) + 1];
    
    void make_pre() {
    	U = (1 << n) - 1;
    	siz[0] = -1; powe[0] = 1;
    	for(int i = 1;i <= U; i++) siz[i] = siz[i >> 1] * (i & 1 ? -1 : 1);
    	for(int i = 1;i <= m; i++) powe[i] = powe[i - 1] * 2 % mod; 
    }
    
    int main() {
    	
    	n = read(); m = read();
    	for(int i = 1, x, y;i <= m; i++) x = read() - 1, y = read() - 1, mp[x][y] = 1;
    	make_pre(); f[0] = 1;
    	for(int i = 0;i < U; i++) {
    		for(int j = 0;j < n; j++) d[1 << j] = 0;
    		for(int j = 0;j < n; j++) if(i & (1 << j)) 
    				for(int k = 0;k < n; k++) d[1 << k] += mp[j][k];
    		int C = U ^ i; sum[0] = 0;			
    		for(int tmp = (C - 1) & C; ; tmp = (tmp - 1) & C) {
    			int now = tmp ^ C, low = now & -now; // tmp ^ C 是因为更新sum要从小往大
    			sum[now] = sum[now - low] + d[low]; // 边数
    			f[i ^ now] = (f[i ^ now] + (1ll * f[i] * powe[sum[now]] % mod * siz[now] % mod + mod) % mod) % mod;
    			if(!tmp) break;
    		}
    	}
    	printf("%d", f[U]);
    	
    	fclose(stdin); fclose(stdout);
    	return 0;	
    }
    
    

    T3

    ​ 题目大意:

    ​ 求满足如下条件的数对(a,b)对数:a,b 均为正整数且 a,b<=n 而lcm(a,b)>n。其中的 lcm 当然表示最小公倍数。答案对 1,000,000,007取模.(n <= 1e10)

    ​ 莫比乌斯反演. 推式子.

    ​ 题目让我们求(frac{a * b}{ gcd(a, b)} > n) 的对数,单步容斥一下,求(n ^ 2 - frac{a * b}{gcd(a, b)} <= n) 的对数.

    ​ 瞎搞一波:

    (ans = displaystyle sum_{d = 1}^{n}sum _{j = 1}^{n}sum_{i = 1}^{n} [ij <= n * d] [gcd(i, j) == d] = sum_{d = 1}^{n}sum _{j = 1}^{lfloor frac{n}{d} floor }sum_{i = 1}^{lfloor frac{n}{d} floor } [i * d * j * d <= n * d] [gcd(i, j) == 1] = sum_{d = 1}^{n}sum _{j = 1}^{lfloor frac{n}{d} floor }sum_{i = 1}^{lfloor frac{n}{d} floor } [ijd<= n] [gcd(i, j) == 1])

    ​ 再瞎搞一波:

    (ans = displaystyle sum_{d = 1}^{n}sum_{j = 1}^{lfloor frac{n}{d} floor} sum_{i = 1}^{lfloor frac{n}{d} floor} sum_{k mid gcd(i, j)} mu(k)[ijd <= n] = sum_{d = 1}^{n} sum_{k = 1}^{lfloor frac{n}{d} floor} mu(k)sum_{j = 1}^{lfloor frac{n}{dk} floor} sum_{i = 1}^{lfloor frac{n}{dk} floor} [ijd *k * k <= n])

    ​ 我们发现:(lfloor frac{n}{dk} floor)以上的部分是没有必要枚举的,所以说(k)只需枚举到(sqrt n)所以上式可变为:(displaystyle sum_{k = 1}^{sqrt n} mu(k) sum _{d = 1}^{n}sum_{j = 1}^{n} sum_{i = 1}^{n} [ijd <= frac{n}{k ^ 2}])

    ​ 我们假定(d < j < i),那么答案将乘6(可以类比一下如果只有两个数a,b, 可以只统计(a < b)的个数,然后乘2).如果说(d, j ,i)这三个数有两个相等,那么答案乘3.

    ​ 然后复杂度我不会分析,网上说用积分可得到函数的时间复杂度是(Θ(n^{frac{2}{3}})).(md什么鬼).

    #include <bits/stdc++.h>
    
    #define int long long
    
    using namespace std;
    
    const int N = 1e5 + 5, mod = 1e9 + 7;
    int cnt, mu[N], prime[N], is_prime[N];
    long long n, res;
    
    void make_mu() {
    	mu[1] = 1;
    	for(int i = 2;i < N; i++) {
    		if(!is_prime[i]) { prime[++ cnt] = i; mu[i] = -1; }
    		for(int j = 1;j <= cnt && i * prime[j] < N; j++) {
    			is_prime[i * prime[j]] = 1;
    			if(!(i % prime[j])) break; 
    			mu[i * prime[j]] = -mu[i];
    		}
    	}
    }
    
    long long get_ans(int x) {
    	long long ans = 0;
    	for(int i = 1;i * i * i <= x; i++) {
    		ans ++; //三个数相等 i * i * i <= x
    		for(int j = i + 1;j * j * i < x; j++) 
    			ans = (ans + 6ll * (x / i / j - j) % mod) % mod; // i是第一个数, j是第二个数, x / i / j是第三个数,减去j是因为强制了第三个数大于第二个数,相当于把第三个数小于等于第二个数的部分减去了
    	}
    	for(int i = 1;i * i <= x; i++) 
    		ans = (ans + 3ll * (x / i / i - (x / i / i >= i)) % mod) % mod; //强制两个数相等,那么第三个数就是x / i / i,然后强制第三个数不等于第一二个数,如果说第三个数大于等于第一二个数,那么将会包含三个数相等的情况,所以减去1.
    	return ans;
    }
    
    signed main() {
    	
    	cin >> n; make_mu();
    	for(int i = 1;i * i <= n; i++) 
    		if(mu[i]) res = (res + (mu[i] * get_ans(n / i / i) % mod + mod) % mod) % mod;
    	n %= mod;
    	printf("%lld", (1ll * n * n % mod - res + mod) % mod);
    	
    	return 0;
    }
    
  • 相关阅读:
    Mayan游戏 (codevs 1136)题解
    虫食算 (codevs 1064)题解
    靶形数独 (codevs 1174)题解
    黑白棋游戏 (codevs 2743)题解
    神经网络 (codevs 1088) 题解
    The Rotation Game (POJ 2286) 题解
    倒水问题 (codevs 1226) 题解
    银河英雄传说 (codevs 1540) 题解
    生日蛋糕 (codevs 1710) 题解
    第一章 1.11 高阶函数
  • 原文地址:https://www.cnblogs.com/czhui666/p/13927693.html
Copyright © 2011-2022 走看看