Processing math: 47%
Codechef 15.10 解题报告
codevs#6 题解

SRM672 div1 题解

shinbokuow posted @ 9 年前 in Something , 1828 阅读

 

Easy

题目大意:

给定一个无穷长的数列,一开始有ai=i。从i2开始,对于所有i|jj>ij进行操作swap(aj,aj+1)

问经过充分长的时间后an的值。

数据范围n1010

题解:

我只能YY出有效的操作次数不是非常多,并且答案波动的范围非常小。

于是写了一个记忆化的因子分解。

然后暴力进行操作。

时间复杂度玄学。

结果在n=2时挂掉了。

官方的解好像就是这样的,但是看别人的代码好像还有别的姿势?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;
 
map<long long, int>M;
vector<long long>v[110];
 
int id;
int check(long long c){
    if(M[c] == 0){
        M[c] = ++id;
        for(int i = 1; (long long)i * i <= c; ++i){
            if(c % i == 0){
                v[id].push_back(i);
                if((long long)i * i != c)
                    v[id].push_back(c / i);
            }
        }
        return id;
    }
    else
        return M[c];
}
int id1, id2;
class Procrastination {
public:
    long long findFinalAssignee(long long n) {
        long long now = 1;
        long long x = n;
        //int cnt = 0;
        while(now < n){
            //++cnt;
            id1 = check(x - 1), id2 = check(x);
            long long min1 = 1ll << 60, min2 = 1ll << 60;
            for(int i = 0; i < v[id1].size(); ++i)
                if(v[id1][i] > now)
                    min1 = min(min1, v[id1][i]);
            for(int i = 0; i < v[id2].size(); ++i)
                if(v[id2][i] > now)
                    min2 = min(min2, v[id2][i]);
            int type = -1;
            long long mn = 1ll << 60;
            if(min1 < x - 1 && min1 < mn){
                mn = min1;
                type = 1;
            }
            if(min2 < x && min2 < mn){
                mn = min2;
                type = 2;
            }
            if(type == -1)
                break;
            if(type == 1){
                --x;
                now = mn;
            }
            else{
                ++x;
                now = mn;
            }
            //cout << x << endl;
        }
        //cout << cnt << endl;
        return x;
    }
};

Medium

题目大意:

求有多少张点数为n的无向图使得至多添加一条边之后存在一条欧拉回路。

输出答案对109+7取模的余数。

数据范围n2000

题解:

首先要求的无向图肯定是连通的。

如果直接就是欧拉回路的话,需要满足所有点的度数都是偶数。

如果需要添加一条边的话,则是有且仅有两个点的度数是奇数。

假设我们已经能够求出gn表示n的点的所有点的度数都是偶数的连通图的个数。

那么答案就是gn×(n(n1)2+1)

因为我们可以枚举哪两个点的度数为奇数,如果这两个点之间没有边则连上一条边,否则删除两个点之间的边。

我们再令fn表示n个点所有点的度数均为偶数的无向图的个数。

这个可以直接计算,我们单独拿出某个点,于是剩下的无向图的个数为2(n1)(n2)2,对于我们得到的每个无向图,显然都会有偶数个度数为奇数的点。那么独立的那个点必须向无向图中所有度数为奇数的点连边,这能够保证独立的点的度数也为偶数,且连边的方法是唯一的。

于是我们就得到了fn=2(n1)(n2)2

显然我们可以再利用补集转化求gn,只需要减去不连通的所有点度数均为偶数的图个数就行了。

对于某个点,枚举这个点所在连通块的大小,递推式是这样的:

g_{n}=f_{n}-\sum_{i=1}^{n-1}\binom{n-1}{i-1}g_{i}f_{n-i}

时间复杂度O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
 
using namespace std;
 
typedef long long ll;
static const int mod = 1000000007;
void inc(int &x, int y){
    if((x += y) >= mod)
        x -= mod;
}
void dec(int &x, int y){
    if((x -= y) < 0)
        x += mod;
}
 
#define N 2010
int C[N][N], Pow[N * N];
int f[N], g[N];
class AlmostEulerianGraph {
public:
    int calculateGraphs(int n) {
        int i, j, k;
        for(Pow[0] = 1, i = 1; i <= n * n; ++i)
            Pow[i] = (Pow[i - 1] + Pow[i - 1]) % mod;
        for(C[0][0] = C[1][0] = C[1][1] = 1, i = 2; i <= n; ++i){
            C[i][0] = C[i][i] = 1;
            for(j = 1; j < i; ++j)
                inc(C[i][j] = C[i - 1][j], C[i - 1][j - 1]);
        }
        f[1] = g[1] = 1;
        for(i = 2; i <= n; ++i){
            f[i] = g[i] = Pow[(i - 1) * (i - 2) >> 1];
            for(j = 1; j < i; ++j)
                dec(g[i], (ll)g[j] * C[i - 1][j - 1] % mod * f[i - j] % mod);
        }
        int ans = (ll)g[n] * (1 + (n * (n - 1) >> 1)) % mod;
        return ans;
    }
};

hard

题目大意:

给定一个边带权的无向完全图,以0号点为根进行Prim算法求最大生成树,当边权相同时随便选一个点加入当前集合。

对于每一个点,你要求出这个点在Prim算法的所有可能情况中最早第几个被加入当前集合。

数据范围n\leq{50},边权范围0\leq{w}<10

题解:

考虑最终形成的最大生成树的形态。

我们可以考虑,先一并加入所有权值为9的边,再一并加入所有权值为8的边,。。。,依此类推。

那么我们这样想,假设我们要求的某个点i通过权值为j的边加入了集合,那么必然有所有权值>j的边对于连通性做出的贡献都已经被考虑过了。

不妨利用动态规划处理集合之间的合并:

对于每个根i,以及每个权值j,我们处理出只考虑>j的权值的i所在的最大连通块,大小为cnt

那么对于这个连通块所有相邻点k,如果边权恰为j,点k就能在最多cnt个点之后加入i所在的集合。

我们可以新建一张有向图,连接(i,k,cnt),于是这个问题转化为了一个图论中的最短路问题。

利用Folyd进行处理。

时间复杂度O(wn^3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
using namespace std;
 
 
void mini(int &x, int y){
    if(x > y)
        x = y;
}
 
#define N 50
bool vis[N];
int f[N][N], w[N][N];
int cnt;
void dfs(int x, int y, int n){
    ++cnt;
    vis[x] = 1;
    for(int j = 0; j < n; ++j){
        if(j != x && !vis[j] && w[x][j] > y)
            dfs(j, y, n);
    }
}
 
class Tdetective {
public:
    int reveal(vector <string> s) {
        int n = s.size();
        int i, j, k, _k;
        for(i = 0; i < n; ++i)
            for(j = 0; j < n; ++j)
                w[i][j] = s[i][j] - '0';
        memset(f, 0x1f, sizeof f);
        for(i = 0; i < n; ++i){
            for(j = 0; j < 10; ++j){
                memset(vis, 0, sizeof vis);
                cnt = 0;
                dfs(i, j, n);
                for(k = 0; k < n; ++k){
                    if(!vis[k])
                        continue;
                    for(_k = 0; _k < n; ++_k)
                        if(!vis[_k] && w[k][_k] == j)
                            mini(f[i][_k], cnt);
                }
            }
        }
        for(k = 0; k < n; ++k)
            for(i = 0; i < n; ++i)
                for(j = 0; j < n; ++j)
                    mini(f[i][j], f[i][k] + f[k][j]);
         
        int ans = 0;
        for(i = 1; i < n; ++i)
            ans += i * f[0][i];
         
        return ans;
    }
};
ymz 说:
6 年前

写得很好!!!sro博主orz

1000 kwh battery 说:
9 个月前

During this site, you will see this shape, I definitely propose you arise as OK with this new development. youibot robot maintenance amr robot


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter