Problem G
k-Colouring of a Graph
You are given a simple graph with N nodes and M edges. The graph has the special property that any connected component of size s contains no more than s+2 edges. You are also given two integers k and P. Find the number of k-colourings of the graph, modulo P.
Recall that a simple graph is an undirected graph with no self loops and no repeated edges. A k-colouring of a graph is a way to assign to each node of the graph exactly one of k colours, such that if edge (u,v) is present in the graph, then u and v receive different colors.
Input
The first line of input consists of four integers, N,M,k, and P (1≤N≤50000, 0≤M≤1.5N, 1≤k≤109, 1≤P≤2⋅109). The next M lines of input each contains a pair of integers A and B (1≤A≤N, 1≤B≤N), describing an edge in the graph connecting nodes A and B.
Output
Output the number of k-colourings of the given graph, modulo P.
Sample Input 1
3 3 2 10000
1 2
2 3
3 1
Sample output 1
0
Sample Input 2
3 3 4 13
1 2
2 3
3 1
Sample output 2
11
题意
N个节点M条边的图着k种颜色(其中M<=N+2),使得每条边连接的两个点是不同的颜色,求着色方案的数量。
这个问题本身是一个NP问题,应该用回溯法来解决。但是网上的这个模板改出来之后TLE,关了输入输出流同步之后还是TLE。我猜应该是这种方法写的…只是需要再优化。不然就是某个鬼畜的数学方法。
下面的超时代码:用邻接表c来表示一个无向连通图G=(V,E)。用整数1,2,…,m来表示m种不同的颜色。x[i]表示顶点i所着的颜色来,则问题的解向量可以表示为n元组x[1:n]。问题的解空间可表示一棵高度为n+1的完全m叉树。解空间树的第i层中每一结点都有m个儿子,每个儿子相应于x[i]的m个可能的着色之一,第n+1层结点均为叶结点。
在回溯算法中,当i>n时,表示算法已搜索至一个叶结点,得到一个新的m着色方案,因此当前已找到的可m着色方案数sum增1。当i≤n时,当前扩展结点Z是解空间树中的一个内部结点。该结点有x[i]=1,2,…,m。对当前扩展结点Z的每一个儿子结点,由函数Ok检查其可行性,并以深度优先的方式递归地对可行子树进行搜索,或剪去不可行子树。
TLE代码
#include <iostream>
#include <vector>
#include <cstdio>
#include <algorithm>
#define ll long long
std::vector <int> c[50010];
ll point_color[5010];
ll cnt = 0;
ll n, m, u, v, g, p;
bool ok(int k) {
for(int i = 1; i < k; i++){
std::vector<int>::iterator result = find(c[k].begin( ), c[k].end( ), i);
if(!(result == c[k].end()) && point_color[i] == point_color[k]) {
return 0;
}
}
return 1;
}
void graphcolor(int n, int m) {
for(int i = 1; i <= n; i++)
point_color[i] = 0;
int k = 1;
while(k >= 1) {
point_color[k] = point_color[k] + 1;
while(point_color[k] <= m){
if (ok(k)) {
break;
} else {
point_color[k]=point_color[k]+1;
}
}
if(point_color[k] <= m && k == n) {
cnt++;
cnt %= p;
}
else if(point_color[k] <= m && k < n) {
k = k + 1;
} else {
point_color[k] = 0;
k = k - 1;
}
}
}
int main(int argc, char *argv[]) {
std::ios::sync_with_stdio(false);
std::cin >> n >> g >> m >> p;
int s, t;
for(int i=1; i <= g ; i++) {
std::cin >> s >> t;
c[s].push_back(t);
c[t].push_back(s);
}
graphcolor(n, m);
std::cout << cnt << std::endl;
return 0;
}
UPDATE
时隔一年之后群里问了这题,qls说是bell(6)暴力枚举三条非树边六个节点的颜色,然后树DP。
复习阶段 好像搞不动 先鸽着。