第三届 方块填数

2016年 第六题

生成树计数

给定一个 nm 的格点图,包含 n 行 m 列共 nm 个顶点,相邻的顶点之间有一条边。
【图1.png】给出了一个3*4的格点图的例子。

如果在图中删除部分顶点和其相邻的边,如上图删除第2行第3列和第3行第1列的顶点后,如【图2.png】所示。

图的生成树指包含图中的所有顶点和其中的一部分边,使得任意两个顶点之间都有由边构成的唯一路径。如果两个生成树包含有不同的边即被认为不同,则上图中共有31种不同的生成树,其中a边不选有10种,a边选有21种。
给出格点图中保留的顶点的信息,请计算该图一共有多少种不同的生成树。

【输入格式】 输入的第一行包含两个整数n, m,用空格分隔,表示格点图的行数和列数。
接下来n行,每行m个字母(中间没有分隔字符),每个字母必然是大写E或大写N,E表示对应的顶点存在,N表示对应的顶点不存在。保证存在至少一个顶点。

【输出格式】 输出一行,包含一个整数,表示生成树的个数。答案可能很大,你只需要计算答案除以1000000007的余数即可。

【样例输入】 3 4 EEEE EENE NEEE

【样例输出】 31

【数据规模与约定】 对于10%的数据,1<=n<=2。 对于30%的数据,1<=n<=3。 对于40%的数据,1<=n<=4。
对于50%的数据,1<=n<=5。 另有20%的数据,1<=n*m<=12。 另有10%的数据,1<=m<=15。
对于100%的数据,1<=n<=6,1<=m<=100000。

资源约定: 峰值内存消耗 < 256M CPU消耗 < 4500ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0 注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。 注意:
所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

  • 代码及思路

生成树计数 解题说明

【试题难度】 10

【算法一】
对于nm<=12的数据,可以发现至多有17条边,直接暴力枚举每条边选不选,然后看边的条数是不是等于存在的点的个数-1,再深搜或广搜判断图是否连通即可。 总时间复杂度:O((2^(2n*m-n-m))nm) 期望得分:20

【算法二】
根据Matrix-Tree定理,一个k个点的无向图的生成树个数等于(它的度数矩阵-它的邻接矩阵)的任何一个k-1阶主子式的值。所以我们可以把问题转化为求一个O(nm)阶行列式的值,可以用初等变换法转化为上三角行列式以快速求出(涉及除法时改为乘以除数模1000000007的逆元即可)。
总时间复杂度:O((n
m)^3) 期望得分:30

【算法三】 对于n=2时:记a(i)为2i的网格图生成树个数,则有a(i) = 4a(i-1) - a(i-2),a(0) =
0,a(1) =
1。所以我们可以预处理出a(1)~a(m),如果输入整个图连通则答案是图中所有连续最大2i网格图答案的积(如第一行EEEEEENEE第二行ENEEEEEEE则答案为a(1)a(4)a(2)=1564=224),否则为0。
总时间复杂度:O(n
m) 期望得分:10(结合算法一30,结合算法二40)

【算法四】
考虑状压dp。定义函数b(i)表示将i个不同元素划分到若干个集合的方案数,则有b(1)=1,b(2)=2,b(3)=5,b(4)=15,b(5)=52,b(6)=203。定义f[i][j]为图中前i列已做完(抉择前i列每条边选不选),第i列的连通性为j(表示哪些行的点属于一个连通块)的方案数,其中j有b(n)种取值。注意到动态规划转移情况只与第i列与第i+1列的2n个点有关,所以我们可以对这2n个点的所有4n种情况预处理转移矩阵(矩阵中第x行y列元素表示f[i+1][x]由f[i][y]转移而来的方案数),转移时可以直接将f[i]列向量左乘对应的转移矩阵(一次乘法O((b(n))2))。但是这样做的空间复杂度是O((4n)*(b(n))2),n=6时会爆内存(其实也会超时)。我们发现这些矩阵中大部分元素是0,把一个数加上另一个数的0倍是没有意义的,所以我们可以不记整个矩阵,而是对每一个行或列开一个vector之类的数据结构存储若干个pair表示原矩阵中一个非零数的位置和大小,这样既省空间也省时间——一次动态规划转移的时间取决于矩阵中非零数的个数。可以发现,2n个“E”对应的矩阵中的非零数尤其多,所以当数据基本全是“E”的时候会运行得比较慢。所以我们可以使用矩阵快速幂,预处理时存下来2n个“E”对应的矩阵,并计算它的2^(1~log(m))次方的矩阵,dp时对于一大段全是“E”的区间通过左乘O(log(区间长度))个矩阵解决。
时间复杂度:预处理:O(b(n)(8n)*n+(b(n))3log(m)),然而常数非常小;dp:O(m*(b(n))^2)),然而常数也非常小……
期望得分:50~100

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
using namespace std;
#define MAXN 6
#define MAXM 101010
#define MAXS 203
#define MAXL 64
#define MAXG 18
#define MOD 1000000007
int n, m, logm;
char s[MAXN][MAXM];
int h[MAXM];
class Status
{
public:
int a[MAXN];
inline bool operator <(const Status &b) const
{
for(int i = 0; i < n; i++)
if(a[i] != b.a[i])
return a[i] < b.a[i];
return false;
}
};
Status t[MAXS];
map <Status, int> p;
int num;
class Matrix
{
public:
int a[MAXS][MAXS];
Matrix operator *(const Matrix &b) const
{
Matrix c;
memset(c.a, 0, sizeof(c.a));
for(int j = 0; j <= num; j++)
{
const int *tmp1 = b.a[j];
for(int i = 0; i <= num; i++)
{
long long tmp2 = a[i][j];
int *tmp3 = c.a[i];
for(int k = 0; k <= num; k++)
tmp3[k] = (tmp3[k] + tmp2 * tmp1[k]) % MOD;
}
}
return c;
}
};
Matrix b[MAXG];
vector <pair <int, int> > c[MAXL][MAXL][MAXS];
class Vector
{
public:
int a[MAXS];
};
Vector ans;
void mul(Vector &a, int x, int y)
{
if(y >= 0)
{
Vector ret;
memset(ret.a, 0, sizeof(ret.a));
for(int i = 0; i <= num; i++)
{
long long tmp1 = a.a[i];
for(vector <pair <int, int> > :: iterator j = c[x][y][i].begin(); j != c[x][y][i].end(); j++)
ret.a[j->first] = (ret.a[j->first] + tmp1 * j->second) % MOD;
}
a = ret;
}
else
{
Vector ret;
Vector *ret1 = &a, *ret2 = &ret;
for(int i = 0; (1 << i) <= x; i++)
if(x & (1 << i))
{
memset(ret2->a, 0, sizeof(ret2->a));
for(int j = 0; j <= num; j++)
{
long long tmp1 = ret1->a[j];
const int *tmp2 = b[i].a[j];
for(int k = 0; k <= num; k++)
ret2->a[k] = (ret2->a[k] + tmp1 * tmp2[k]) % MOD;
}
swap(ret1, ret2);
}
if(&a != ret1)
a = *ret1;
}
}
void init()
{
num = 0;
memset(t[0].a, 0, sizeof(t[0].a));
p[t[0]] = 0;
while(t[num].a[n - 1] != n - 1)
{
t[num + 1] = t[num];
num++;
int i;
for(i = n - 1; i > 0; i--)
{
bool flag = false;
for(int j = i - 1; j >= 0; j--)
if(t[num].a[j] >= t[num].a[i])
{
flag = true;
break;
}
if(flag)
{
t[num].a[i]++;
break;
}
}
for(i++; i < n; i++)
t[num].a[i] = 0;
p[t[num]] = num;
}
Status tmp;
int tmp2;
for(int i = 0; i <= num; i++)
{
for(int j = 0; j < (1 << n); j++)//down
{
for(int k = 0; k < (1 << (n - 1)); k++)//right
{
tmp2 = 0;
bool ok = true;
for(int i1 = 0; i1 < n; i1++)
{
if((j >> i1) & 1)
{
bool flag = true;
for(int j1 = 0; j1 < i1; j1++)
if(t[i].a[j1] == t[i].a[i1] && ((j >> j1) & 1))
{
tmp.a[i1] = tmp.a[j1];
flag = false;
break;
}
if(i1 != 0 && ((k >> (i1 - 1)) & 1))
{
if(!flag)
{
if(tmp.a[i1 - 1] == tmp.a[i1])//circle
{
ok = false;
break;
}
else
{
int tmp3 = tmp.a[i1 - 1];
for(int j1 = i1 - 1; j1 >= 0; j1--)
{
if(tmp.a[j1] == tmp3)
tmp.a[j1] = tmp.a[i1];
else if(tmp.a[j1] > tmp3)
tmp.a[j1]--;
}
tmp2--;
}
}
else
tmp.a[i1] = tmp.a[i1 - 1];
}
else if(flag)
tmp.a[i1] = tmp2++;
}
else if(i1 != 0 && ((k >> (i1 - 1)) & 1))
tmp.a[i1] = tmp.a[i1 - 1];
else
tmp.a[i1] = tmp2++;
}
if(ok)
{
tmp2 = 0;//ignore some points in the previous line
for(int i1 = 0; i1 < n; i1++)//number
{
int tmp3 = 0;//count
bool flag = true;
for(int j1 = 0; j1 < n; j1++)
{
if(t[i].a[j1] == i1)
{
tmp3++;
if((j >> j1) & 1)
{
flag = false;
break;
}
}
}
if(!tmp3)//max number
break;
if(flag)//ignore
{
if(tmp3 != 1)//ignored points mustn't have edges
{
ok = false;
break;
}
else
{
for(int j1 = 0; j1 < n; j1++)
if(t[i].a[j1] == i1)
{
tmp2 |= (1 << j1);
break;
}
}
}
}
if(ok)
{
int tmp3 = 0;//some points to be ignored in this line
for(int i1 = 0; i1 < n; i1++)
if(!((j >> i1) & 1) && (i1 == 0 || !((k >> (i1 - 1)) & 1)) && (i1 == n - 1 || !((k >> i1) & 1)))
tmp3 |= (1 << i1);
if(tmp2 == 0)
b[0].a[i][p[tmp]]++;
int tmp4 = p[tmp], tmp5 = tmp3;
for(; true; tmp3 = ((tmp3 - 1) & tmp5))
{
bool flag = true;
for(vector <pair <int, int> > :: iterator it = c[tmp2][tmp3][i].begin(); it != c[tmp2][tmp3][i].end(); ++it)
{
if(it->first == tmp4)
{
it->second++;
flag = false;
break;
}
else if(it->first > tmp4)
{
c[tmp2][tmp3][i].insert(it, make_pair(tmp4, 1));
flag = false;
break;
}
}
if(flag)
c[tmp2][tmp3][i].push_back(make_pair(tmp4, 1));
if(tmp3 == 0)
break;
}
//printf("%d: %d->%d: %d, %d\n", tmp2, i, p[tmp], j, k);
}
}
}//k
}//j
}//i
/*freopen("tmp.txt", "w", stdout);
for(int i = 0; i < (1 << n); i++)
{
printf("\n%d:\n", i);
for(int j = 0; j <= num; j++)
{
for(int k = 0; k <= num; k++)
printf("%4d", a[i].a[j][k]);
printf("\n");
}
}*/
/*for(int i = 1; i < (1 << n); i++)
for(int j = 0; j <= num; j++)
for(int k = 0; k <= num; k++)
if(a[i].a[j][k])
c[i][j][d[i][j]++] = make_pair(k, a[i].a[j][k]);*/
//for(int i = 0; i <= num; i++)
// ans.a[i] = a[num].a[num][i];
memset(ans.a, 0, sizeof(ans.a));
ans.a[num] = 1;
for(int i = 0; i < n; i++)
scanf("%s", s[i]);
int m1 = 0;
for(int i = 0; i < m; i++, m1++)
{
h[m1] = 0;
for(int j = 0; j < n; j++)
if(s[j][i] == 'N')
h[m1] |= (1 << j);
if(m1 == 0 && h[m1] == (1 << n) - 1)
m1--;
//printf("%d %d\n", i, m1);
}
while(h[m1 - 1] == (1 << n) - 1)
m1--;
m = m1;
//printf("%d\n", m);
logm = 0;
tmp2 = m;
while(tmp2 >>= 1)
logm++;
for(int i = 1; i <= logm; i++)
b[i] = b[i - 1] * b[i - 1];
}
int main()
{
scanf("%d%d", &n, &m);
init();
mul(ans, (1 << n) - 1, h[0]);
/*for(int j = 0; j <= num; j++)
printf("%d ", ans.a[j]);
printf("\n");*/
for(int i = 0; i < m - 1; i++)
{
if(h[i] || h[i + 1])
mul(ans, h[i], h[i + 1]);
else
{
int tmp = i - 1;
while(i + 3 < m && !h[i + 2])
i++;
mul(ans, i - tmp, -1);
}
/*printf("%d %d %d: ", i, h[i], h[i + 1]);
for(int j = 0; j <= num; j++)
printf("%d ", ans.a[j]);
printf("\n");*/
}
Status tmp;
int tmp2 = 0, tmp3 = -1;
for(int i = 0; i < n; i++)
if((h[m - 1] >> i) & 1)
tmp.a[i] = tmp2++;
else if(tmp3 == -1)
tmp.a[i] = tmp3 = tmp2++;
else
tmp.a[i] = tmp3;
printf("%d\n", ans.a[p[tmp]]);
return 0;
}
/*
3 4
EEEE
EENE
NEEE

6 6
NENENN
NENEEN
NENNEN
NEEEEN
NENENN
NENEEN

1 6
NNEENN
*/

2017年 第六题

观光铁路
【问题描述】
跳蚤国正在大力发展旅游业,每个城市都被打造成了旅游景点。许多跳蚤想去其他城市旅游,但是由于跳得比较慢,它们的愿望难以实现。这时,小C听说有一种叫做火车的交通工具,在铁路上跑得很快,便抓住了商机,创立了一家铁路公司,向跳蚤国王请示在每两个城市之间都修建铁路。然而,由于小C不会扳道岔,火车到一个城市以后只能保证不原路返回,而会随机等概率地驶向与这个城市有铁路连接的另外一个城市。
跳蚤国王向广大居民征求意见,结果跳蚤们不太满意,因为这样修建铁路以后有可能只游览了3个城市(含出发的城市)以后就回来了,它们希望能多游览几个城市。于是跳蚤国王要求小C提供一个方案,使得每只跳蚤坐上火车后能多游览几个城市才回来。
小C提供了一种方案给跳蚤国王。跳蚤国王想知道这个方案中每个城市的居民旅游的期望时间(设火车经过每段铁路的时间都为1),请你来帮跳蚤国王。
【输入格式】
输入的第一行包含两个正整数n、m,其中n表示城市的数量,m表示方案中的铁路条数。
接下来m行,每行包含两个正整数u、v,表示方案中城市u和城市v之间有一条铁路。
保证方案中无重边无自环,每两个城市之间都能经过铁路直接或间接到达,且火车由任意一条铁路到任意一个城市以后一定有路可走。
【输出格式】
输出n行,第i行包含一个实数ti,表示方案中城市i的居民旅游的期望时间。你应当输出足够多的小数位数,以保证输出的值和真实值之间的绝对或相对误差不超过1e-9。
【样例输入】
4 5
1 2
2 3
3 4
4 1
1 3
【样例输出】
3.333333333333
5.000000000000
3.333333333333
5.000000000000
【样例输入】
10 15
1 2
1 9
1 5
2 3
2 7
3 4
3 10
4 5
4 8
5 6
6 7
6 10
7 8
8 9
9 10
【样例输出】
10.000000000000
10.000000000000
10.000000000000
10.000000000000
10.000000000000
10.000000000000
10.000000000000
10.000000000000
10.000000000000
10.000000000000
【数据规模与约定】
对于10%的测试点,n ≤ 10;
对于20%的测试点,n ≤ 12;
对于50%的测试点,n ≤ 16;
对于70%的测试点,n ≤ 19;
对于100%的测试点,4 ≤ k ≤ n ≤ 21,1 ≤ u, v ≤ n。数据有梯度。

  • 代码
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 24;
const int table[MAXN][MAXN][32][2] = {{{0}},{{0},{1}},{{0},{1},{1}},{{0},{2},{1},{1}},{{0},{{10,8},{1,5},{3,7}}
,{2},{1},{1}},{{0},{{12,9},{1,5},{2,7},{4,8}}
,{2},{1},{1},{1}},{{0},{{15,9},{1,5},{2,7},{3,10},{4,8},{6,10},{9,10}}
,{{12,10},{1,6},{3,8}}
,{2},{1},{1},{1}},{{0},{{16,11},{1,5},{1,8},{3,7},{4,9},{6,10}}
,{{14,10},{1,6},{2,11},{4,9},{8,11}}
,{2},{2},{1},{1},{1}},{{0},{{18,12},{1,5},{2,7},{3,9},{4,11},{6,10},{8,12}}
,{{16,10},{1,6},{2,11},{3,8},{5,12},{9,12},{11,12}}
,{{14,12},{1,7},{4,10}}
,{2},{1},{1},{1},{1}},{{0},{{21,12},{1,5},{1,8},{2,10},{3,7},{3,13},{4,11},{6,10},{9,13},{12,13}}
,{{18,12},{1,6},{2,9},{3,13},{5,10},{7,13},{11,13}}
,{{15,13},{1,7},{3,10}}
,{2},{2},{1},{1},{1},{1}},{{0},{{23,14},{1,5},{1,8},{2,11},{3,7},{3,13},{4,9},{5,12},{6,10},{10,14}}
,{{21,14},{1,6},{2,11},{3,8},{4,13},{5,10},{7,12},{9,14}}
,{{17,13},{1,7},{2,14},{5,11},{10,14}}
,{{16,14},{1,8},{4,11}}
,{2},{2},{1},{1},{1},{1}},{{0},{{26,15},{1,5},{1,8},{2,10},{2,13},{3,7},{4,9},{4,12},{6,13},{7,11},{9,14},{11,15}}
,{{22,15},{1,6},{2,11},{3,8},{4,13},{5,10},{7,12},{9,14}}
,{{18,15},{1,7},{2,11},{5,13}}
,{{17,15},{1,8},{4,11}}
,{2},{2},{1},{1},{1},{1},{1}},{{0},{{28,16},{1,5},{1,8},{1,12},{2,14},{3,7},{3,11},{4,9},{4,15},{6,10},{6,14},{9,13},{10,16}}
,{{24,16},{1,6},{1,10},{2,13},{3,8},{4,11},{4,15},{7,12},{9,14}}
,{{20,15},{1,7},{2,11},{4,16},{5,13},{9,16}}
,{{19,14},{1,8},{2,15},{5,12},{11,16},{15,16}}
,{{18,16},{1,9},{5,13}}
,{2},{2},{1},{1},{1},{1},{1}},{{0},{{31,17},{1,5},{1,8},{1,11},{2,14},{3,7},{3,12},{3,16},{4,9},{5,15},{6,10},{6,13},{9,14},{10,16},{13,17}}
,{{26,16},{1,6},{1,10},{2,13},{3,8},{3,17},{4,11},{5,14},{7,12},{9,14},{15,17}}
,{{22,16},{1,7},{2,11},{4,17},{6,13},{9,17},{15,17}}
,{{20,16},{1,8},{2,17},{5,14},{12,17}}
,{{19,17},{1,9},{4,13}}
,{2},{2},{2},{1},{1},{1},{1},{1}},{{0},{{34,18},{1,5},{1,8},{1,11},{2,13},{2,16},{3,7},{4,9},{4,12},{5,15},{6,10},{6,13},{7,17},{9,14},{10,16},{12,17},{14,18}}
,{{29,18},{1,6},{1,10},{2,15},{3,8},{3,12},{4,17},{5,14},{7,16},{9,14},{11,16},{13,18}}
,{{23,18},{1,7},{1,12},{4,10},{5,14},{9,16}}
,{{22,16},{1,8},{2,17},{4,18},{5,14},{10,18},{12,17}}
,{{21,15},{1,16},{3,17},{6,18},{8,16},{11,17},{13,18}}
,{{20,18},{1,10},{5,14}}
,{2},{2},{1},{1},{1},{1},{1},{1}},{{0},{{38,19},{1,5},{1,8},{2,10},{2,13},{3,7},{3,15},{4,9},{4,12},{5,16},{6,14},{6,18},{7,11},{8,17},{9,14},{10,18},{11,16},{12,19},{13,17},{15,19}}
,{{31,18},{1,6},{1,10},{2,13},{3,8},{3,16},{4,11},{4,19},{6,15},{7,12},{9,14},{12,17},{14,19},{18,19}}
,{{25,19},{1,7},{2,13},{4,16},{5,11},{8,15},{10,18}}
,{{24,16},{1,8},{2,17},{4,18},{6,13},{10,18},{12,17},{15,19},{18,19}}
,{{22,17},{1,9},{2,18},{6,14},{13,19},{18,19}}
,{{21,19},{1,10},{5,14}}
,{2},{2},{2},{1},{1},{1},{1},{1},{1}},{{0},{{41,20},{1,5},{1,8},{1,11},{2,13},{2,17},{3,7},{3,19},{4,12},{4,16},{5,14},{6,10},{6,17},{7,15},{8,18},{9,13},{9,16},{10,19},{11,15},{12,18},{14,19},{16,20}}
,{{34,20},{1,6},{1,10},{2,13},{2,17},{3,8},{4,11},{4,15},{5,18},{7,12},{7,16},{9,14},{9,18},{12,19},{15,20}}
,{{27,20},{1,7},{2,11},{3,15},{5,18},{8,14},{10,17},{13,19}}
,{{25,20},{1,8},{2,15},{4,11},{6,17},{12,19}}
,{{23,19},{1,9},{2,20},{6,16},{14,20}}
,{{23,16},{1,17},{3,18},{6,19},{8,20},{11,18},{14,19},{17,20}}
,{{22,20},{1,11},{6,16}}
,{2},{2},{2},{1},{1},{1},{1},{1},{1}},{{0},{{44,21},{1,5},{1,8},{1,11},{2,13},{2,17},{3,15},{3,19},{4,9},{4,12},{5,18},{6,10},{6,13},{7,16},{7,19},{8,14},{9,17},{9,20},{10,15},{11,19},{12,16},{13,20},{14,18},{15,21}}
,{{36,20},{1,6},{1,10},{2,13},{2,17},{3,8},{3,21},{4,11},{4,15},{5,18},{7,12},{7,16},{9,14},{9,18},{12,19},{15,20},{19,21}}
,{{29,21},{1,7},{2,11},{3,15},{5,18},{6,13},{8,16},{10,19},{14,20}}
,{{27,20},{1,8},{2,15},{3,21},{5,18},{6,13},{10,17},{11,21}}
,{{25,17},{1,9},{2,18},{4,19},{6,14},{11,20},{13,21},{18,21},{19,20}}
,{{24,18},{1,10},{2,19},{6,15},{14,20},{19,21},{20,21}}
,{{23,21},{1,11},{5,16}}
,{2},{2},{2},{1},{1},{1},{1},{1},{1},{1}}};
class Railway
{
public:
static const double EPS = 1e-9;
static const int MAXN = 30, oo = 101010101;

private:
double a[MAXN][MAXN];
int x[MAXN];
int deg[MAXN];
int dist[MAXN][MAXN];

public:
double ans[MAXN];
vector<int> to[MAXN];
int n, m;
int cmp(double x)
{
return x > EPS ? 1 : x >= -EPS ? 0 : -1;
}

private:
void add(int now, int u, int v, int dest)
{
const int du = deg[u], dv = deg[v];
if(v == dest)
return;
if(u == dest)
{
a[now][v] -= (double)dv / du / (dv - 1);
a[now][n + 1] -= 1.0 / du / (dv - 1);
return;
}
if(du == 2 && dv == 2)
{
a[now][n + 1] += 0.5;
add(now, v, to[v][0] == u ? to[v][1] : to[v][0], dest);
return;
}
a[now][v] -= (double)(du - 1) * dv / du / (du * dv - du - dv);
a[now][now] += 1.0 / (du * dv - du - dv);
a[now][n + 1] -= (double)(du - 2) / du / (du * dv - du - dv);
}

public:
Railway()
{
n = m = 0;
}
void clear()
{
for(int i = 1; i <= n; i++)
to[i].clear();
m = 0;
}
bool insert(int u, int v)
{
if(u < 1 || u > n || v < 1 || v > n)
return false;
to[u].push_back(v);
to[v].push_back(u);
m++;
return true;
}
bool valid()
{
for(int i = 1; i <= n; i++)
if((deg[i] = to[i].size()) < 2)
{
cerr << "degree[" << i << "] = " << deg[i] << endl;
return false;
}
memset(dist, 31, sizeof(dist));
for(int i = 1; i <= n; i++)
dist[i][i] = 0;
for(int i = 1; i <= n; i++)
for(int j = 0; j < deg[i]; j++)
{
if(dist[i][to[i][j]] <= 1)
{
cerr << "edge " << i << " " << to[i][j] << endl;
return false;
}
dist[i][to[i][j]] = 1;
}
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(dist[i][j] > dist[i][k] + dist[k][j])
dist[i][j] = dist[i][k] + dist[k][j];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(dist[i][j] >= oo)
{
cerr << "disconnect: " << i << " " << j << endl;
return false;
}
return true;
}
bool solve()
{
if(!valid())
return false;
for(int dest = 1; dest <= n; dest++)
{
memset(a, 0, sizeof(a));
for(int i = 1; i <= n; i++)
{
a[i][i] = 1;
a[i][n + 1] = 1;
for(vector<int>::iterator it = to[i].begin(); it != to[i].end(); it++)
add(i, i, *it, dest);
}
for(int i = 1; i <= n; i++)
x[i] = i;
for(int i = 1; i <= n; i++)
{
int j = i;
while(j <= n && cmp(a[x[j]][i]) == 0)
j++;
if(j > n)
return false;
swap(x[i], x[j]);
for(j = i + 1; j <= n; j++)
{
double mul = a[x[j]][i] / a[x[i]][i];
if(cmp(mul) == 0)
continue;
for(int k = i; k <= n + 1; k++)
a[x[j]][k] -= a[x[i]][k] * mul;
}
}
for(int i = n; i >= 1; i--)
{
double now = a[x[i]][n + 1] / a[x[i]][i];
if(i == dest)
ans[i] = now;
for(int j = i - 1; j >= 1; j--)
a[x[j]][n + 1] -= a[x[j]][i] * now;
}
}
return true;
}
void write() const
{
for(int i = 1; i <= n; i++)
printf("%.12lf\n", ans[i]);
}
void printedge()
{
printf("%d\n", m);
for(int i = 1; i <= n; i++)
for(vector <int> :: iterator it = to[i].begin(); it != to[i].end(); it++)
if(*it > i)
printf("%d %d\n", i, *it);
}
}railway;


const double Railway::EPS;

int n, k;
void insert_circle(int len)
{
for(int i = 1; i < len; i++)
railway.insert(i, i + 1);
railway.insert(1, len);
}
void solve()
{
railway.clear();
scanf("%d", &n);
railway.n = n;
scanf("%d", &k);
for(int i = 1; i <= k; i++)
{
int u, v;
scanf("%d%d", &u, &v);
railway.insert(u, v);
}
if(!railway.solve())
printf("error\n");
railway.write();
}
int main()
{
solve();
return 0;
}

2015年 第六题

标题:模型染色

在电影《超能陆战队》中,小宏可以使用他的微型机器人组合成各种各样的形状。
现在他用他的微型机器人拼成了一个大玩具给小朋友们玩。为了更加美观,他决定给玩具染色。

小宏的玩具由n个球型的端点和m段连接这些端点之间的边组成。下图给出了一个由5个球型端点和4条边组成的玩具,看上去很像一个分子的球棍模型。
由于小宏的微型机器人很灵活,这些球型端点可以在空间中任意移动,同时连接相邻两个球型端点的边可以任意的伸缩,这样一个玩具可以变换出不同的形状。在变换的过程中,边不会增加,也不会减少。
小宏想给他的玩具染上不超过k种颜色,这样玩具看上去会不一样。如果通过变换可以使得玩具变成完全相同的颜色模式,则认为是本质相同的染色。现在小宏想知道,可能有多少种本质不同的染色。

【输入格式】 输入的第一行包含三个整数n, m, k, 分别表示小宏的玩具上的端点数、边数和小宏可能使用的颜色数。端点从1到n编号。
接下来m行每行两个整数a, b,表示第a个端点和第b个端点之间有一条边。输入保证不会出现两条相同的边。

【输出格式】 输出一行,表示本质不同的染色的方案数。由于方案数可能很多,请输入方案数除10007的余数。

【样例输入】 3 2 2 1 2 3 2 【样例输出】 6 【样例说明】 令(a, b,
c)表示第一个端点染成a,第二个端点染成b,第三个端点染成c,则下面6种本质不同的染色:(1, 1, 1), (1, 1, 2), (1,
2, 1), (1, 2, 2), (2, 1, 2), (2, 2, 2)。 而(2, 1, 1)与(1, 1, 2)是本质相同的,(2,
2, 1)与(1, 2, 2)是本质相同的。

【数据规模与约定】 对于20%的评测数据,1<=n<=5, 1<=k<=2。 对于50%的评测数据,1<=n<=10, 1<=k<=8。
对于100%的评测数据,1<=n<=10, 1<=m<=45, 1<=k<=30。

资源约定: 峰值内存消耗 < 512M CPU消耗 < 5000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0 注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。 注意:
所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

代码

思路:本题是一个比较复杂的计数问题,需要使用多种颜色对图进行染色。
如果使用搜索方法,可以获得本题比较基础的20分,搜索比较复杂,需要判断图的同构。
本题一种正确的做法是使用组合数学的Polya定理。这个定理需要对于给定的图求出对于这个图的所有置换。在本题中可以使用枚举所有可能的排列,判断这个排列是否与原图同构来判断是否是一个置换。求完置换后求出每个置换循环节数量,利用Polya定理可以求解。
本题需要用到比较复杂的知识,同构的判断容易出错,可能有少量考生可以做出本题。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>

using namespace std;

const int MAXN = 100;
const int MOD = 10007;
int n, m, k;
int g[MAXN][MAXN];
int mp[MAXN], q[MAXN];
long long tt[MAXN];
long long pw[MAXN];

bool isIsomorphism(int p)
{
for (int i = 1; i < p; ++i)
if (g[p][i] ^ g[q[p]][q[i]])
return false;
return true;
}

void isomorphismSearch(int p, int cnt)
{
if (p > n)
{
tt[cnt]++;
return ;
}
for (int i = p; i <= n; ++i)
{
swap(q[p], q[i]);
if (isIsomorphism(p))
{
int r = q[p];
while (r<p)
r = q[r];
isomorphismSearch(p+1, cnt+(r==p));
}
swap(q[p], q[i]);
}
}

void solve(long long a, long long b, long long c, long long &x, long long &y)
{
if (b==0)
{
x = c/a; y = 0;
return ;
}
solve(b, a%b, c, y, x);
y -= a/b * x;
}

int main()
{
cin >> n >> m >> k;
for (int i = 0; i < m; ++i)
{
int a, b;
cin >> a >> b;
g[a][b] = g[b][a] = 1;
}
for (int i = 1; i <= n; ++i)
q[i] = i;
pw[0] = 1;
for (int i = 1; i <= n; ++i)
pw[i] = (pw[i-1]*k) % MOD;

memset(tt, 0, sizeof(tt));
isomorphismSearch(1, 0);
long long ans = 0, stt = 0;
for (int i = 1; i <= n; ++i)
{
ans += ((tt[i]%MOD)*pw[i])%MOD;
stt += tt[i];
}
ans %= MOD;
long long x, y;
solve(stt%MOD, MOD, ans, x, y);
x %= MOD;
if (x < 0)
x += MOD;
cout << x << endl;
return 0;
}