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;
    }
    
  • 相关阅读:
    CSS3 经典教程系列:CSS3 线性渐变(linear-gradient)
    JS定义函数
    CSS选择器和jQuery选择器的区别与联系
    jQuery 选择器、遍历方法
    jQuery中$()函数
    JS对象和Jquery对象
    [Alpha]Scrum Meeting#2
    [Alpha]Scrum Meeting#1
    knowledge_docker
    problems_docker
  • 原文地址:https://www.cnblogs.com/czhui666/p/13927693.html
Copyright © 2011-2022 走看看