今天下午,我们队做了Pku Campus 2010,还算可以4个1y,做了6题,罚时挺少的。过了A,C,D,E,G,H ^_^
A 我当时找规律的,发现有递推式,然后得推O(1)公式才能过... 1Y
Poj 3761 找规律 别人有推公式的
#include
<
cstdio
>
#include
<
cstring
>
#include
<
algorithm
>
#include
<
vector
>
#include
<
queue
>
#include
<
map
>
#include
<
cmath
>
#include
<
cstdlib
>
#include
<
iostream
>

using
namespace
std;

const
int
INF
=
1000000000
;
const
int
MAXN
=
1000001
;
const
int
MOD
=
20100713
;

void
bruteForce()

{
for(int n = 1 ; n < 10 ; n++)

{
vector<int> vt;
for(int i = 0 ; i < n ; i++)
vt.push_back(i);

int cnt[10] =
{0};
do

{
vector<int>_vt(vt);
//bubble sort
int round = 0;
for(int i = 0 ; i < n ; i++ , round++)

{
bool flag = false;
for(int j = 1 ; j < n ; j++)
if(vt[j] < vt[j-1])

{
swap(vt[j-1],vt[j]);
flag = true;
}
if(flag == false)break;
}
cnt[round]++;
vt = _vt;
}
while( next_permutation(vt.begin() , vt.end()) );
printf("n %d:\n",n);
for(int i = 0 ; i < n ; i++)
printf("%d %d\n",i,cnt[i]);
puts("");
}
}

/**/
/*
k\ n 1 2 3 4 5 6
0 1 1 1 1 1 1
1 0 1 3 7 15 31
2 0 2 10 38 130
3 0 6 42 222
4 0 24 216
5 0 120

可以发现:
k
dp[n,k] = k*dp[n-1,k] + ∑dp[n-1,j]
0
这样子计算是O(n*k)的,TLE ,需要O(1)的公式
dp[n,0] = 1
dp[n,1] = 2^(n-1) - 1
自然推出
dp[n,2] = 2dp[n-1,2] + dp[n-1,2] + dp[n-1,1] + dp[n-1,0]
= 3dp[n-1,2] + 2^(n-2)
这个可以用特征方程推出dp[n,2] = 2*3^(n-2) - 2^(n-1)
同理,就能知道
dp[n,3] =3dp[n-1,3] + dp[n-1,3] + dp[n-1,2] +dp[n-1,1]+dp[n-1,0]
= 4dp[n-1,3] + 2*3^(n-3)
推出dp[n,3] = 2*3*4^(n-3) - 2*3^(n-2)
对比猜想
dp[n,0] = 1
dp[n,1] = 2^(n-1) - 1
dp[n,2] = 2*3^(n-2) - 2^(n-1)
dp[n,3] = 2*3*4^(n-3) - 2*3^(n-2)

dp[n,k] = k! * ( (k+1)^(n-k) - k^(n-k))

http://roba.rushcj.com/?p=491
下面的留言那里有人推公式的 Orz
*/


long
long
ipow(
long
long
a,
int
n)

{
if(n==0)return 1;
if(n==1)return a % MOD;
long long ans = ipow(a,n/2);
ans = ans * ans % MOD;
if(n&1)ans = ans*a%MOD;
return ans;
}

long
long
f[MAXN];

int
main()

{

#ifdef ONLINE_JUDGE
#else
// freopen("in", "r", stdin);
// freopen("out", "w", stdout);
#endif

//bruteForce();

//需要先预处理阶乘
f[0] = 1;
for(int i = 1 ; i < MAXN ;i++)
f[i] = f[i-1] * i % MOD;
int n , k , T;
for(scanf("%d",&T) ; T-- ; )

{
scanf("%d%d",&n,&k);
printf("%lld\n" , f[k] * ((ipow(k+1,n-k) - ipow(k,n-k)) + MOD) % MOD);
}
return 0;
}

C 树形dp 1Y
Poj 3763 ★★★
/**/
/*
题意:给出一棵树,问添加最少的边使得每个点走出一个哈密顿回路(每个点只走一次,且边不重复走)
树形dp,一开始状态设计不清晰,想着既然是环,就定义把整棵子树搞成环的最小代价,发现需要多一个搞成链的状态
其实,只需要定义把子树搞成链的最小代价即可
dp[u]把子树搞成链,u为链的一个端点
_dp[u]把子树搞成链,u为链的中间点

处理一棵树时,其实就是串链子,把子树的链串起来
对于dp[u],可以选择dp[v],_dp[v] ,还有要把num个儿子串起来,需要加num-1条边串起来
即∑min{dp[v],_dp[v]} + num - 1 但这样子有可能都是_dp[v],即根u不与子树连起来,
本来就该选择一个_dp[v]去掉,加上dp[v] 或者直接对某个_dp[v]+1,发现效果都是一样的,最多只增加1,
所以不用枚举选择一个_dp[v]去掉换上dp[v],直接加上1即可!

同理,对于_dp[u] ,∑min{dp[v],_dp[v]} + num - 1
看选了多少个_dp[v] ,不够的话要补链

最后的答案是对dp[1],_dp[1]补成环
dp[1] , 即1是一个链的端点,就要看之前选了多少个dp[v]了,多于1个的话,就不用加边了
_dp[1] 就得加多一条边,连接两个子树

*/

#include
<
cstdio
>
#include
<
cstring
>
#include
<
algorithm
>
#include
<
vector
>
#include
<
queue
>
#include
<
map
>
#include
<
cmath
>
#include
<
cstdlib
>
#include
<
iostream
>

using
namespace
std;

const
int
INF
=
1000000000
;
const
int
MAXN
=
100010
;

struct
Edge

{
int v;
Edge *next;
}
;

Edge
*
E[MAXN] ,
*
pE , edges[MAXN
*
2
];
int
dp[MAXN] , _dp[MAXN];
int
cnt[MAXN];

void
add(
int
u ,
int
v)

{
pE->v = v;
pE->next = E[u];
E[u] = pE++;
}

void
dfs(
int
u,
int
p)

{
cnt[u] = 0;
int num = 0 , tot = 0;
for(Edge *it = E[u] ; it ; it = it->next)

{
int v = it->v;
if(v == p)continue;
num ++;
dfs(v,u);
if(dp[v] <= _dp[v])

{
cnt[u] ++;
tot += dp[v];
}
else tot += _dp[v];
}
if(num == 0)dp[u] = _dp[u] = 0;
else

{
dp[u] = num - 1 + tot;
_dp[u] = num - 2 + tot;
if(cnt[u] < 1)dp[u] ++;
if(cnt[u] < 2)_dp[u] += (2-cnt[u]);
}
// printf("%d %d %d %d\n",u,cnt[u],dp[u],_dp[u]);
}

int
main()

{

#ifdef ONLINE_JUDGE
#else
freopen("in", "r", stdin);
#endif

int n;
while( ~scanf("%d",&n) )

{
pE = edges;
for(int i = 1; i <= n ; i++)
E[i] = NULL;
int u ,v;
for(int i = 1 ; i < n ;i++)

{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs(1,1);
int A = dp[1] + (cnt[1] <= 1);//写成cnt[u]错了一次 #_#
int B = _dp[1] + 1;
printf("%d\n",min(A,B));
}
return 0;
}

D 预处理dist[] 用Trie树加速枚举查找 1TLE 1 RE 1 WA
Poj 3764 ★★★★ 树路径上异或值最大
/**/
/*
题意 : 求一棵树上某条路径,其边权的异或值最大
比赛时我想到先以0为根dfs出每个点到0的路径的异或值dist[u], 然后要求两点间路径的异或值就是 dist[a] ^ dist[b]
这样问题就转化为怎么在这个dist[]里面找出最大的异或值、自身值

起初的一个贪心的思路:
最高位为1的肯定要选,而且不能被异或掉,所以按照最高位来分组
这样就枚举最高的那一组的那些值去跟其它组的值异或,找最大
同时,对于x,要找y,使得异或后增大的话,找的只需是x的最左的0那一位,但y该位是1的,这样必定会增大值
如 x = 1101101 , 找最高位为4的那一组,该组为空的话就找第1组,然后只需枚举这一组的y跟x的异或值即可

但这样子做还是会超时,这样的枚举只会优化常数

比赛时zeus提出用Trie树做,这样子枚举时复杂度就很低了,果然,在Trie树走就可以了

做法是,对剩余那些组(最高位那组不用加进去,它是用来枚举的)建立Trie树
然后对最高位那组的所有值在Trie树上走,先选择使得异或值为1的点走,没有就只好走异或值为0的了

原来《浅谈信息学竞赛中的“0”和“1”》有提到建立Trie树搞

“对某个值s,要查找n个权值中和其异或最大的,trie树确实是最佳选择”
*/
#include
<
cstdio
>
#include
<
cstring
>
#include
<
algorithm
>
#include
<
vector
>
#include
<
queue
>
#include
<
map
>
#include
<
cmath
>
#include
<
cstdlib
>
#include
<
iostream
>

using
namespace
std;

const
int
INF
=
1000000000
;
const
int
MAXN
=
100010
;


struct
Edge

{
int v , w;
Edge *next;
}
;

Edge
*
E[MAXN] ,
*
pE , edges[MAXN
*
2
];

struct
Node

{
Node *ch[2];
}
;

Node
*
pN , nodes[MAXN
*
31
];

Node
*
newNode()

{
for(int i = 0 ; i < 2; i++)
pN->ch[i] = NULL;
return pN++;
}

int
dist[MAXN];

void
dfs(
int
u ,
int
p ,
int
W)

{
dist[u] = W;
for(Edge *it = E[u] ; it ; it = it->next)

{
int v = it->v , w = it->w;
if(v == p)continue;
dfs(v,u,w^W);
}
}

void
add(
int
u ,
int
v ,
int
w)

{
pE->v = v;
pE->w = w;
pE->next = E[u];
E[u] = pE++;
}


void
insert(Node
*
root ,
int
len ,
int
x)

{
if(len < 0)return;
bool who = (x>>len)&1 ;
if(root->ch[who] == NULL )root->ch[who] = newNode();
insert(root->ch[who] , len-1 , x);
}

int
gao(Node
*
root ,
int
len ,
int
x)

{
if(root == NULL)return x;
if(len < 0 )return x;
bool who = (x>>len)&1;
who ^= 1;
if(root->ch[who] == NULL )who ^= 1;
return gao(root->ch[who] , len-1 , who ? x ^ (1<<len) : x );
}

int
main()

{

#ifdef ONLINE_JUDGE
#else
freopen("in", "r", stdin);
#endif

int n ;
while( ~scanf("%d",&n) )

{
pE = edges;
pN = nodes;
for(int i = 0 ; i< n ; i++)
E[i] = NULL;

int u ,v ,w;
for(int i =1 ;i < n ; i++)

{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}

dfs(0,0,0);

vector<int> vt[32];
for(int i = 0 ; i < n ; i++)

{
if(dist[i] == 0)continue;
for(int j = 30 ; j >= 0 ; j--)

{
if( dist[i] & (1<<j) )

{
vt[j].push_back(dist[i]);
break;
}
}
}

int high = 30;
while(high >=0 && vt[high].size() == 0) high--;
if(high < 0)

{
puts("0");
}
else

{
Node *root = newNode();//insert all the highest < high
for(int i = high - 1 ; i >= 0 ; i--)

{
for(int j = 0 , jsize = vt[i].size() ; j < jsize ; j++)

{
insert(root , 30 , vt[i][j]);
}
}
int ans = 0 , size = vt[high].size();
for(int i = 0 ; i < size ; i++)

{
int x = vt[high][i] ;
ans = max(ans , x);
ans = max(ans , gao(root , 30 , x) );
}
printf("%d\n" ,ans);
}
}
return 0;
}

E,G,H 略
更详细的报告见:
http://blog.csdn.net/yuhailin060/archive/2010/05/10/5576255.aspx
http://roba.rushcj.com/?p=491
A 我当时找规律的,发现有递推式,然后得推O(1)公式才能过... 1Y
Poj 3761 找规律 别人有推公式的

















































































































































C 树形dp 1Y
Poj 3763 ★★★



































































































































D 预处理dist[] 用Trie树加速枚举查找 1TLE 1 RE 1 WA
Poj 3764 ★★★★ 树路径上异或值最大



















































































































































































































E,G,H 略
更详细的报告见:
http://blog.csdn.net/yuhailin060/archive/2010/05/10/5576255.aspx
http://roba.rushcj.com/?p=491