生成树计数 NOI2007

这题一定要把状态认识清楚,因为只选最后K个点的连通性作为状态,所以一个状态可能会对应很多的连边的情况,由于无法找到特殊的状态吧初始情况地推出来,所以初始情况需要暴力求解,然后再用矩阵加速。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string>    
#include <sstream>
#include <utility>   
#include <ctime>

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::stringstream;
using std::make_pair;
using std::getline;
using std::greater;
using std::endl;
using std::multimap;
using std::deque;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PAIR;
typedef multimap<int, int> MMAP;

const int MAXN(6010);
const int MAXM(5010);
const int MAXE(10010);
const int HSIZE(13131);
const int SIGMA_SIZE(26);
const int MAXH(19);
const int INFI((INT_MAX-1) >> 1);
const ULL BASE(31);
const LL LIM(10000000);
const int INV(-10000);
const int MOD(65521);

template<typename T>
inline T ABS(const T &op){return op < 0? -op: op;}

LL gcd(LL a, LL b)
{
	LL temp;
	while(b)
	{
		temp = a%b;
		a = b;
		b = temp;
	}
	return a;
}
LL lcm(LL a, LL b){return a/gcd(a, b)*b;}

struct MAT
{
	int r, c;
	LL arr[60][60];
	MAT(int tr, int tc): r(tr), c(tc)
	{
		memset(arr, 0, sizeof(arr[0])*r);
	}
	MAT()
	{}
	void reset()
	{
		memset(arr, 0, sizeof(arr[0])*r);
	}
	void operator = (const MAT &op)
	{
		r = op.r;
		c = op.c;
		memcpy(arr, op.arr, sizeof(arr[0])*r);
	}
	void identity()
	{
		reset();
		for(int i = 0; i < r; ++i)
			arr[i][i] = 1;
	}
};

void mat_mul(const MAT &op1, const MAT &op2, MAT &re)
{
	re.r = op1.r;
	re.c = op2.c;
	re.reset();
	for(int i = 0; i < op1.c; ++i)
		for(int j = 0; j < op1.r; ++j)
			for(int k = 0; k < op2.c; ++k)
				re.arr[j][k] = (re.arr[j][k]+op1.arr[j][i]*op2.arr[i][k])%MOD;
}

MAT t1, t2;

void mat_pow(const MAT &op, LL n, MAT &re)
{
	t1 = op;
	re.r = op.r;
	re.c = op.c;
	re.identity();
	for(int i = 0; (1LL << i) <= n; ++i)
	{
		if(n&(1LL << i))
		{
			t2 = re;
			mat_mul(t1, t2, re);
		}
		mat_mul(t1, t1, t2);
		t1 = t2;
	}
}

MAT mat, re, tmat;

struct HASH_MAP
{
	int first[HSIZE], next[MAXN];
	int state[MAXN];
	int value[MAXN];
	int size;
	void init()
	{
		memset(first, -1, sizeof(first));
		size = 0;
	}
	int insert(int ts, int tv)
	{
		int h = ts%HSIZE;
		for(int i = first[h]; ~i; i = next[i])
			if(state[i] == ts)
			{
				value[i] += tv;
				return i;
			}
		value[size] = tv;
		state[size] = ts;
		next[size] = first[h];
		first[h] = size;
		return size++;
	}
} hm1, hm2;

int K;
int tcode[5], code[5], Num[8];

void decode(int ts)
{
	for(int i = 0; i < K; ++i)
	{
		tcode[i] = ts&7;
		ts >>= 3;
	}
}

int encode()
{
	int ret = 0, cnt = 0;
	memset(Num, -1, sizeof(Num));
	for(int i = K-1; i >= 0; --i)
	{
		if(Num[code[i]] == -1) Num[code[i]] = cnt++;
		ret = (ret << 3)|Num[code[i]];
	}
	return ret;
}

bool vis[5];

void updata()
{
	int cnt = 0;
	for(int i = 0; i < K; ++i)
		if(tcode[i] == tcode[0]) ++cnt;
	int lim = (1 << K)-1;
	for(int i = 0; i <= lim; ++i)
	{
		if(cnt == 1 && (i&1) == 0) continue;
		memset(vis, 0, sizeof(vis));
		bool flag(true);
		int num = 7;
		for(int j = 0; j < K; ++j)
			if(i&(1 << j))
			{
				if(vis[tcode[j]])
				{
					flag = false;
					break;
				}
				num = tcode[j];
				vis[tcode[j]] = true;
			}
		if(flag)
		{
			for(int j = 1; j < K; ++j)
				code[j-1] = tcode[j];
			for(int j = 0; j < K-1; ++j)
				if(vis[code[j]])
					code[j] = num;
			code[K-1] = num;
			hm2.insert(encode(), 1);
		}
	}
}

void process(int ind)
{
	hm2.init();
	decode(hm1.state[ind]);
	updata();
	for(int i = 0; i < hm2.size; ++i)
	{
		int temp = hm1.insert(hm2.state[i], 0);
		mat.arr[ind][temp] = hm2.value[i];
	}
}

void dfs(int dep)
{
	if(dep == K)
	{
		hm1.insert(encode(), 0);
		return;
	}
	for(int i = 0; i <= dep; ++i)
	{
		code[dep] = i;
		dfs(dep+1);
	}
}

struct FIND_SET
{
	int fa[5];
	void init()
	{
		for(int i = 0; i < 5; ++i)
			fa[i] = i;
	}
	int find(int sour){return sour == fa[sour]? sour: fa[sour] = find(fa[sour]);}
	bool Union(int a, int b)
	{
		a = find(a);
		b = find(b);
		if(a == b) return false;
		fa[b] = a;
		return true;
	}
} fs;

int conn[5];
bool closure[5][5];

void dfs2(int dep)   //求解初始状态
{
	if(dep == K)
	{
		fs.init();
		memset(closure, 0, sizeof(closure));
		for(int i = 0; i < K; ++i)
		{
			for(int j = i+1; j < K; ++j)
				if(conn[i]&(1 << (K-1-j)))
				{
					if(!fs.Union(i, j)) return;
					closure[i][j] = true;
					closure[j][i] = true;
				}
		}
		for(int i = 0; i < K; ++i)
			for(int j = 0; j < K; ++j)
				for(int k = 0; k < K; ++k)
					closure[j][k] |= closure[j][i]&closure[i][k];
		int cnt = 0;
		memset(code, -1, sizeof(code));
		for(int i = 0; i < K; ++i)
		{
			if(code[i] == -1) 
				code[i] = cnt++;
			else
				continue;
			for(int j = i+1; j < K; ++j)
				if(closure[i][j])
					code[j] = code[i];
		}
		int temp = hm1.insert(encode(), 0);
		tmat.arr[0][temp] += 1;
		return;
	}
	int lim = (1 << (K-1-dep))-1;
	for(int i = 0; i <= lim; ++i)
	{
		conn[dep] = i;
		dfs2(dep+1);
	}
}


void solve(LL n)
{
	hm1.init();
	dfs(0);
	tmat.r = 1;
	tmat.c = hm1.size;
	for(int i = 0; i < hm1.size; ++i)
		tmat.arr[0][i] = 0;
	dfs2(0);
	mat.r = mat.c = hm1.size;
	mat.reset();
	for(int i = 0; i < hm1.size; ++i)
		process(i);
	mat_pow(mat, n-K, re);
	mat_mul(tmat, re, mat);
	printf("%lld\n", mat.arr[0][0]);
}

int main()
{
	LL n;
	while(~scanf("%d%lld", &K, &n))
	{
		solve(n);
	}
	return 0;
}


你可能感兴趣的:(生成树计数 NOI2007)