T1 高一学堂
高一学堂,因为有了 y x r yxr yxr,就成了现在这个样子 = =。由于 y x r yxr yxr 的语言太过雷人,每次他发微往往都会有一石激起千层浪的效果,具体就是所有关注他的人都会转发,同时 @ @ @他,接着关注这些人的人也会转发,同时 @ @ @他关注的人(注意转发内容本身会有 @ @ @ y x r yxr yxr),以此类推。这样导致每次 y x r yxr yxr 发微博都会被 @ @ @上兆次,而 y x r yxr yxr 又特别喜欢发,sina 支持不了如此庞大的数据量,特出规定,每次转发时, @ @ @的人不能超过K 人,好友在转发时如果超过了,就把最早那人删掉。现在 y x r yxr yxr 刚发了一条微博“求满分”,他想知道每个与他有联系的人分别会被 @ @ @多少次。
输入格式:
输入第一行有三个整数,
N
N
N,
K
K
K,表示人数和
K
K
K。
接下来
N
−
1
N-1
N−1 行,每行有2 个整数
a
a
a,
b
b
b,表示
a
a
a 和
b
b
b 有关注关系。
输入给出一棵以
1
1
1 号点为根的树,一号点代表
y
x
r
yxr
yxr,对于任意一个点,他的儿子都关注他。
输出格式:
输出有 N N N 行,每行有一个整数,这个人会被 @ @ @ 多少次。
样例输入:
5 2
1 2
2 3
2 4
4 5
样例输出:
3
3
0
1
0
数据范围:
对于
30
%
30\%
30%的数据,
N
N
N ≤100;
对于
60
%
60\%
60%的数据,
N
N
N ≤2000,
K
K
K ≤100;
对于
100
%
100\%
100%的数据,
N
N
N ≤100000,
K
K
K ≤
N
N
N。
题解:
- 其实就是求每个结点深度差不超过 K K K 的儿子个数
- 每个结点的儿子个数 - 它 k + 1 k+1 k+1 层每个儿子的儿子个数
#include <bits/stdc++.h>
using namespace std;
vector<int> a[100010];
vector<int> b[100010];
int d[100010],h[100010],k;
void dfs(int u,int fa,int dep)
{
d[u]=dep;
for(int i=0; i<a[u].size(); i++)
{
int v=a[u][i];
if(v==fa) continue;
dfs(v,u,dep+1);
h[u]+=h[v]+1;
}
}
void dfs_2(int u,int fa,int dep)
{
b[d[u]].push_back(u); // 当前子树下,此深度的结点
if(b[dep-k-1].size()&&dep-k-1>0) // 减掉它k+1儿子结点的儿子个数
{
for(int i=0; i<b[dep-k-1].size(); i++)
{
int v=b[dep-1-k][i];
h[v]=h[v]-h[u]-1;
}
}
for(int i=0; i<a[u].size(); i++)
{
int v=a[u][i];
if(v==fa) continue;
dfs_2(v,u,dep+1);
}
// 回溯时把当前结点删除
vector<int>::iterator it;
for(it=b[d[u]].begin();it!=b[d[u]].end();it++)
if(*it==u)
{
b[d[u]].erase(it);
break;
}
}
int main()
{
int n,i,x,y;
scanf("%d%d",&n,&k);
for(i=1; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x].push_back(y);
a[y].push_back(x);
}
dfs(1,0,1); // 记录深度和儿子结点的个数
dfs_2(1,0,1);
for(i=1; i<=n; i++) printf("%d\n",h[i]);
return 0;
}
T2 高二学堂
Wayne 有一副加强版的扑克牌,强大到任意取一个自然数x,在牌堆里都恰
有
4
4
4 张数值为
x
x
x 的牌。每次,Wayne 随机生成两个正整数
n
n
n 和k,然后在牌堆里选取不超过
k
k
k 张牌,使得牌面数字之和恰为
n
n
n。已知Wayne 玩了若干盘,每盘都算出了对应的方案数,他想请你也算出各盘方案数,以验算他的结果是否正确。结果可能比较大,你只需要求出方案数
m
o
d
mod
mod 1000000009(1e9+9) 的值即可。
输入格式:
输入文件包含不超过
10
10
10 组数据。
每行包含两个整数,表示上文中的
n
n
n 和
k
k
k。
输入数据以两个
0
0
0 表示结束。
输出格式:
输出文件中,每组数据输出一行,为对应的方案数。
样例输入:
2 1
2 2
2 3
50 5
0 0
样例输出:
4
10
10
1823966
数据范围:
对于
10
%
10\%
10%的数据,
k
k
k=1;
对于
20
%
20\%
20%的数据,
n
n
n ≤10,
k
k
k ≤4;
对于
40
%
40\%
40%的数据,
n
n
n ≤1000;
对于
60
%
60\%
60%的数据,
n
n
n ≤100000;
对于另外
20
%
20\%
20% 的数据,只有
1
1
1 组数据;
对于
100
%
100\%
100%的数据,
n
n
n ≤10^9,
k
k
k ≤10。
题解
- 不好意思目前本蒟蒻只会 40 40 40分做法,等以后会 100 100 100分了再回来更新(也许你等不到这一天)
- (题意)选取不超过 k k k 个数,每个数可以取至多 4 4 4个,不考虑 0 0 0,使得数字之和为 n n n
- d p [ i ] [ j ] dp[ i ][ j ] dp[i][j] : 和为 i i i ,取 j j j 个数的方案数
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
long long d[100010][12],c[10][10];
int main()
{
int n,k,i,j,l;
memset(d,0,sizeof(d));
c[4][1]=c[4][3]=4;
c[4][2]=6;
c[4][4]=1;
d[0][0]=1;
for(i=1; i<=1000; i++) // 当前正在取 i
for(j=10;j>=1;j--) // 取 j 个数
for(l=0; l<=1000; l++) // 和为 l
for(k=1; k<=min(4,j)&&k*i+l<=1000; k++) // i 取 k 个
d[k*i+l][j]=(d[k*i+l][j]+(d[l][j-k]*c[4][k])%mod)%mod;
while(scanf("%d%d",&n,&k)!=EOF)
{
if(!n&&!k) break;
long long s=0;
for(i=1; i<=k; i++) s=(s+d[n][i])%mod;
printf("%lld\n",s);
}
return 0;
}
T3 高三楼
一个
n
∗
n
n * n
n∗n 的矩阵,其中,每行每列都有两个特殊的点。
"重复"的定义为矩阵
a
a
a ,通过任意次行列变换,变成了矩阵
b
b
b,矩阵
a
a
a ,
b
b
b就视为重复。
例如:对于
3
∗
3
3*3
3∗3 的矩阵,其中矩阵
a
a
a 与矩阵
b
b
b 被视为“重复”矩阵。
对于一个
n
n
n,可以有多少种不“重复”的矩阵,输出答案
m
o
d
mod
mod100000007(1e8+7) 的值。
输入格式:
第一行,一个整数
t
t
t,表示数据组数。
接下来
t
t
t 行,每行一个整数
n
n
n,表示一组数据。
输出格式:
T
T
T 行,每行一个整数,表示方案数。由于答案可能很大,只需要输出方案数
m
o
d
100
,
000
,
007
mod 100,000,007
mod100,000,007 的值就可以了。
样例输入:
3
2
3
4
样例输出:
1
1
2
数据范围:
对于
10
%
10\%
10%的数据
N
N
N ≤
5
5
5;
对于
50
%
50\%
50%的数据
N
N
N ≤
150
150
150;
对于
100
%
100\%
100%的数据
T
T
T ≤
5
5
5 ,
N
N
N ≤
2000
2000
2000
题解
- 此题解法神奇,因为每行每列只有两个标记,所以我们可以把行和列,想成一个二分图的两条边
- 两条边之间的连线等价于某一个标记
- 两条边上的点的移动等价于标记的行列变换
- 所以我们只要考虑有多少种不同的连法即可
- e.g. (可以手模以下
n
=
5
n = 5
n=5)
- 然后——惊奇地发现 ( ๑ ╹ ◡ ╹ ) ノ (๑╹◡╹)ノ (๑╹◡╹)ノ 每 2 ∗ x ( 1 < x < = n ) 2*x(1 < x <= n) 2∗x(1<x<=n)点就会构成一个环
- 每种环的组成,代表一种方案,排列并不重要
- So—— n = a 1 + a 2 + . . . . . . + a m n = a_1+a_2+......+a_m n=a1+a2+......+am ( a m > 1 ) (a_m > 1) (am>1)
- 于是就转化成了正整数拆分的问题 ︿ (  ̄ ︶  ̄ ) ︿ ︿( ̄︶ ̄)︿ ︿( ̄︶ ̄)︿
#include <bits/stdc++.h>
using namespace std;
const int mod=1e8+7;
int d[100010];
int main()
{
int i,j;
memset(d,0,sizeof(d));
d[0]=1;
for(i=2;i<=2000;i++)
for(j=i;j<=2000;j++) d[j]=(d[j]+d[j-i])%mod;
// j = i + ( j - i )
scanf("%d",&i);
while(i--)
{
scanf("%d",&j);
printf("%d\n",d[j]);
}
return 0;
}