2159: Crash 的文明世界


2159: Crash 的文明世界

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 548   Solved: 256
[ Submit][ Status][ Discuss]

Description

Crash 小朋友最近迷上了一款游戏——文明5(Civilization V)。在这个游戏中,玩家可以建立和发展自己的国家,通过外交和别的国家交流,或是通过战争征服别的国家。现在Crash 已经拥有了一个N 个城市的国家,这些城市之间通过道路相连。由于建设道路是有花费的,因此Crash 只修建了N-1 条道路连接这些城市,不过可以保证任意两个城市都有路径相通。在游戏中,Crash 需要选择一个城市作为他的国家的首都,选择首都需要考虑很多指标,有一个指标是这样的:  其中S(i)表示第i 个城市的指标值,dist(i, j)表示第i 个城市到第j 个城市需要经过的道路条数的最小值,k 为一个常数且为正整数。因此Crash 交给你一个简单的任务:给出城市之间的道路,对于每个城市,输出这个城市的指标值,由于指标值可能会很大,所以你只需要输出这个数mod 10007 的值。

Input

输入的第一行包括两个正整数N 和k。下面有N-1 行,每行两个正整数u、v (1 ≤ u, v ≤ N),表示第u 个城市和第v 个城市之间有道路相连。这些道路保证能符合题目的要求。

Output

输出共N 行,每行一个正整数,第i 行的正整数表示第i 个城市的指标值 mod 10007 的值。

Sample Input

5 2
1 2
1 3
2 4
2 5

Sample Output

10
7
23
18
18

HINT

20%的数据满足N ≤ 5000、k ≤ 30。 50%的数据满足N ≤ 50000、k ≤ 30。 100%的数据满足N ≤ 50000、k ≤ 150。 【特别注意】由于数据大小限制为5MB,我只好对测试时的输入文件进行压缩处理。下面的函数可以将压缩的输入文件转化为原始输入文件。(函数从infile 中读入压缩的输入文件,将解压缩后的输入文件输出到outfile 中) C/C++版本: void Uncompress(FILE *infile, FILE *outfile) { int N, k, L, i, now, A, B, Q, tmp; fscanf(infile, "%d%d%d", &N, &k, &L); fscanf(infile, "%d%d%d%d", &now, &A, &B, &Q); fprintf(outfile, "%d %d\n", N, k); for (i = 1; i < N; i ++) { now = (now * A + B) % Q; tmp = (i < L) ? i : L; fprintf(outfile, "%d %d\n", i - now % tmp, i + 1); } } Pascal 版本: procedure Uncompress(var infile, outfile : text); var N, k, L, i, now, A, B, Q, tmp : longint; begin read(infile, N, k, L, now, A, B, Q); writeln(outfile, N, ' ', k); for i := 1 to N - 1 do begin now := (now * A + B) mod Q; if i < L then tmp := i else tmp := L; writeln(outfile, i - now mod tmp, ' ', i + 1); end; end; 下面给出一个具体的例子。civiliazation_compressed.in 表示压缩的输入文件, civilization.in 表示解压缩后的输入文件。 civilization_compressed.in 7 26 4 29643 2347 5431 54209 civilization.in 7 26 1 2 2 3 2 4 3 5 4 6 5 7



2016.2.19重设时限为10s


恕我直言,我还是第一次见这么奇葩的题目描述

都告诉了输入格式,结果最后给你来一团像乱码一样的东西告诉你这才是真正的输入格式...

就这,我re了两次.... 辣鸡题面,降我ac率(掀桌


看到i^k这种格式,以及该题性质,会想到第二类斯特林数.

其中表示第二类斯特林数.

第二类斯特林数表示把i个数分成j个无序集合的个数.

那么


因为单考虑第i个数,可以把这个数分别放入前j-1个集合有j-1种方案,还可以单独一个集合 1种方案

而c是可以递推的,令f[i][j]表示在第i个点disi为j的和

那么对于一个点x,他所能到达更远的所有点一定是能到他的距离+ 1 那么可以直接用组合数算出来

那么考虑从叶子节点向上递推可以算出根节点f值。

然后再从根节点向下推,此时要注意原本从某个点转移过来的方案数.

另外因为有减法,还要 + p再 % p

c++代码如下:

#include<bits/stdc++.h>
#define rep(i,x,y) for(register int i = x; i <= y;++ i)
#define repd(i,x,y) for(register int i = x; i >= y;-- i)
using namespace std;
typedef long long ll;
template<typename T>inline void read(T&x)
{
	x = 0;char c;int sign = 1;
	do { c = getchar(); if(c == '-') sign = -1; }while(!isdigit(c));
	do { x = x * 10 + c - '0'; c = getchar(); }while(isdigit(c));
	x *= sign;
}

const int p = 1e4+7,N = 5e4+500,K = 155;
int s[K][K],f[N][K],ans[N],out[N],fa[N],F[K],t[K],n,k;
int head[N],nxt[N<<1],to[N<<1],tot;

inline void add(int x,int y)
{
	to[tot] = y;
	nxt[tot] = head[x];
	head[x] = tot++;
}

void dfs(int x)
{
	for(register int i = head[x];~i;i = nxt[i])
		if(to[i] != fa[x])
		{
			fa[to[i]] = x;
			out[x] ++;
			dfs(to[i]);
		}
}

void Uncompress(FILE *infile, FILE *outfile)
{
	int N, k, L, i, now, A, B, Q, tmp;
	fscanf(infile, "%d%d%d", &N, &k, &L);
	fscanf(infile, "%d%d%d%d", &now, &A, &B, &Q);
	fprintf(outfile, "%d %d\n", N, k);
	for (i = 1; i < N; i ++)
	{
		now = (now * A + B) % Q;
		tmp = (i < L) ? i : L;
		fprintf(outfile, "%d %d\n", i - now % tmp, i + 1);
	} 
}

int main()
{
	memset(head,-1,sizeof head);
	int L,now,tmp,A,B,Q;
	read(n); read(k); read(L);
	read(now); read(A); read(B); read(Q); 
	rep(i,1,n - 1)
	{
		int u,v;
		now = (now * A + B) % Q;
		tmp = (i < L) ? i : L;
		u = i - now % tmp,v = i + 1;
		add(u,v); add(v,u);
	}
	
	dfs(1);
	
	F[1] = 1;
	rep(i,2,k) F[i] = 1LL*F[i - 1]*i%p;
	s[0][0] = 1;
	rep(i,1,k)
		rep(j,0,i)
			if(j) s[i][j] = (1LL*s[i - 1][j] * j % p + s[i - 1][j - 1]) % p; 
			else s[i][j] = 1LL*s[i - 1][j] * j % p;
	
	queue<int>q;
	
	rep(i,1,n) if(!out[i]) q.push(i);
	while(!q.empty())
	{
		int x = q.front();q.pop();f[x][0]++;
		int y = fa[x];
		if(y)
		{
			out[y] --;
			if(!out[y]) q.push(y); 
			rep(i,0,k)
				if(i) f[y][i] = (f[y][i] + f[x][i - 1] + f[x][i]) % p;
				else f[y][i] = (f[y][i] + f[x][i])%p;
		}
	}
		
	q.push(1);
	while(!q.empty()){
		int x = q.front();q.pop();
		rep(i,1,k)
			ans[x] = (1LL*s[k][i] * F[i] % p * f[x][i] % p + ans[x])%p;
		
		for(register int i = head[x];~i;i = nxt[i])
			if(to[i] != fa[x]){
				rep(j,0,k)
					if(j == 0) t[j] = f[x][j];
					else if(j == 1) t[j] = ((f[to[i]][j] + f[x][j] + f[x][j - 1] - (f[to[i]][j] + f[to[i]][j - 1]) - f[to[i]][j - 1])%p+p)%p;
					else t[j] = ((f[to[i]][j] + f[x][j] + f[x][j - 1] - (f[to[i]][j] + f[to[i]][j - 1]) - (f[to[i]][j - 1] + f[to[i]][j - 2]))%p + p)%p;
				memcpy(f[to[i]],t,sizeof t);
				q.push(to[i]);
		}
	}
	
	rep(i,1,n) printf("%d\n",ans[i]);
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值