NOIP 模拟题
题目名称兔子被子蚊子
源程序文件名rabbit.cpp quilt.cpp mosquito.cpp
输入文件名rabbit.in quilt.in mosquito.in
输出文件名rabbit.out quilt.out mosquito.out
每个测试点时限1s 1s 5s
内存限制512MB 512MB 512MB
测试点数目10 10 10
每个测试点分值10 10 10
是否打开O2 优化
否否否
在windows 下用lemon 进行测试.
AK 了不要婊出题人,没AK 也不要婊出题人.
今天状态非常不好… 本来说四校联考发挥好点的, 事实上发现自己还是暴露了很多问题.
Rabbit
兔子(rabbit)
【题目描述】
做一只明媚的兔子…
兔子都比较喜欢蹦蹦跳跳.但是蹦蹦跳跳的时候如果一直往高处跳的话就太累了,如
果一直往低处跳的话就太无聊了.所以兔子希望跳的时候能够往上跳一步,往下跳一步,
往上跳一步,往下跳一步….一共经过n个高度互不相同的位置(只要向上跳和向下跳相间
分布就可以了,第一步可以往上跳也可以往下跳).如果下一个位置的高度比前一个位置
高,就是往上跳,比前一个位置低,就是往下跳.
兔子今天又蹦蹦跳跳依次经过了n 个位置.现在它想知道经过的n 个位置的高度有
多少种不同的可能.
我们认为n 个位置的高度形成了1 到n 的一个排列,这个排列要么满足奇数项的高
度比相邻位置都大, 要么满足偶数项的高度比相邻位置都大.
n=1 时,有1 种可能,就是这1 个位置的高度为1
n=2 时,有2 种可能,可以是(1,2)或(2,1)
n=3 时,有4 种可能,(1,3,2) (2,3,1),(2,1,3),(3,1,2)
答案可能很大,只需要输出答案对mod 取模的结果.
【输入格式】
一行两个整数n,mod
【输出格式】
一行一个整数ans,表示所有可能的排列数目对mod 取模后的结果.
【样例输入1】
3 1000000007
【样例输出1】
4
【数据范围】
第1,2 个测试点,5<=n<=10
第3,4 个测试点,11<=n<=13
第5,6,7 个测试点,14<=n<=16
第8,9 个测试点,100<=n<=200
第10 个测试点,n=2000
对于所有测试点,mod 在int 范围内
题解
暴力的少见使用… 本来想的是f[i][j][0/1]表示第i个位置值为j, 是升是降的方案数. 后来发现无法知道当前j在之前出没出现过, 所以就放弃打了个暴力. 算是这次唯一比较明智的选择吧. 暴力就是dfs, 然后打表过前6个点, 如果比赛一开始写快点的话能打出第7个点.
正解发现其实与原来的差不多, 用了一种很巧妙的理解方式避免了之前的方案也有j的情况. 首先奇和偶的方案数肯定是一样的, 所以我们只需要考虑其中一种计算即可. 答案乘2即可. f[i][j]表示第i个数选j的方案数, 如果当前上升的话则可从f[i-1][j - 1, j-2…]等转移过来, 下降反之. 现在考虑j在之前有没有出现过. 假设j == i那么从之前之前的转移过来肯定没问题. 假设j < i, 那么我们只需要假想成前面i-1的排列大于等于j的全部都+1, 那么当前就也是一个合法的i的排列. 所以不会出现重复的情况. 这样是n ^ 3的.
稍微改一下dp状态, f[i][j]表示第i个数选1~j的方案数, 这样f[i][j] = f[i][j-1] + f[i-1][i-j], 每次只考虑j这样就是n^2的了, 相较于之前的就是一个前缀和优化. 为什么是f[i-1][i-j]?之前是升当前就是降, 状态总是反的. 也可以说成把原来i-1的合法排列每个数取个反, 就可以迎合当前的j了.
#include<stdio.h>
typedef long long dnt;
const int maxn = 2005;
int n, mod;
dnt f[maxn][maxn];
int main(){
freopen("rabbit.in", "r", stdin);
freopen("rabbit.out", "w", stdout);
scanf("%d%d", &n, &mod);
f[1][1] = 1;
for(int i = 2; i <= n; ++i)
for(int j = 1; j <= i; ++j)
f[i][j] = (f[i][j - 1] + f[i - 1][i - j]) % mod;
printf("%I64d\n", (f[n][n] << 1) % mod);
}
Quilt
被子(quilt)
【题目描述】
作为一只明媚的兔子,需要学会叠被子…
被子是方形的,上面有很多小写字母.可以认为被子是一个n*m 的字符矩阵
被子能够被叠起来,当且仅当每一行,每一列都是回文串.
兔子可以把同一条被子上任意两个位置的字母交换位置,而且兔子不嫌麻烦,为了把
被子叠起来它愿意交换任意多次.但是兔子不能交换两条不同的被子之间的字母.
现在兔子翻箱倒柜找出来了很多被子,请你帮兔子判断每条被子能否被叠起来.
【输入格式】
第一行一个Q,表示被子的条数
接下来描述Q 条被子.
描述每条被子时,第一行输入两个整数n,m 表示由n 行m 列组成
接下来n 行每行一个长度为m 的字符串.字符串中只含小写字母.
【输出格式】
Q 行,依次输出对每条被子的判断结果.如果可以叠起来,输出一行“Yes”(不包括引
号),如果叠不起来,输出一行“No”(不包括引号).
【样例输入】
5
3 4
aabb
aabb
aacc
2 2
aa
bb
5 1
t
weet
2 5
abxba
abyba
1 1
z
【样例输出】
Yes
No
Yes
No
Yes
【数据范围】
第1,2 个测试点,n*m<=10
第3,4 个测试点,n=1
第5 个测试点,n=2
第6 个测试点,n,m 均为偶数
第7,8,9,10个测试点,捆绑测试,无特殊限制.只有通过这全部4个测试点才能拿到这4 个测试点的分数.
所有测试点,保证Q<=10,n,m<=200
题解
特判即可. 因为发现对于偶x偶的来说, 因为每行每列都要对称, 那么每个字符出现的次数都应该是4的倍数.
对于奇x偶的情况, 只有奇中间那一行可以是%4 余 2的. 因为中间那一串自己成为一个回文串即可. 当然出现奇数次数的不行. 同时余2的个数必须要<列/2(如果列为偶的话, 其他反之).
对于奇x奇的情况, 允许一个奇数出现, 拿一个作为中间那一个. 其他余2的次数不能超过(n - 1 + m - 1) / 2.
#include<stdio.h>
const int maxn = 205;
int n, m, T;
char s[maxn];
int cnt[30], mod[10];
inline bool check(){
int num = n % 2 + m % 2;
if(!num){
if(mod[2] || mod[1]) return false;
return true;
}
int lim;
if(num == 1){
if(mod[1]) return false;
if(n & 1) lim = m / 2;
else lim = n / 2;
if(mod[2] <= lim) return true;
else return false;
}
if(num == 2){
if(mod[1] > 1 || !mod[1]) return false;
if(mod[2] <= (n + m - 2) / 2) return true;
else return false;
}
}
int main(){
freopen("quilt.in", "r", stdin);
freopen("quilt.out", "w", stdout);
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
for(int i = 0; i <= 4; ++i) mod[i] = 0;
for(int i = 0; i <= 26; ++i) cnt[i] = 0;
for(int i = 1; i <= n; ++i){
scanf("%s", s + 1);
for(int j = 1; j <= m; ++j)
cnt[s[j] - 'a']++;
}
for(int i = 0; i <= 26; ++i){
int mmod = cnt[i] % 4;
if(mmod == 1) mod[1] ++;
if(mmod == 2) mod[2] ++;
if(mmod == 3) mod[2] ++, mod[1] ++;
}
if(check()) puts("Yes");
else puts("No");
}
return 0;
}
/*
5
3 4
aabb
aabb
aacc
2 2
aa
bb
5 1
t
w
e
e
t
2 5
abxba
abyba
1 1
z
*/
蚊子(mosquito)
【题目描述】
作为一只明媚的兔子,要会叠被子,又得会打蚊子…
兔子住在兔子洞里.兔子洞可以看成是一棵无根树,有n 个洞穴,有n-1 条通道连接
着n 个洞穴.
每天晚上,兔子会在1 号洞穴里缩成一团,睡一觉.同时,蚊子大军出动,去欺负兔子.
因为蚊子人多势众,所以它们分兵m*(m-1)路.m 是整个兔子洞中只和一条通道相
邻的洞穴数目.任意两个这样的洞穴a,b 之间(也就是任意两个叶子节点之间)会有两只
蚊子,一只从a 飞到b,一只从b 飞到a.它们都沿着a 到b 的最短路径移动.蚊子每秒钟可
以通过一条通道.所有蚊子都在0s 时突然出现在起点并开始移动.每只蚊子在到达终点
后的一瞬间都会突然消失.有些蚊子并不会经过兔子所在的1 号节点,它们起到的是恐吓
作用.
又一次满脸是包地醒来后,兔子忍无可忍了,于是它找到liu_runda 让liu_runda 去
打蚊子…liu_runda不知所措,于是去某宝搞了一个灭蚊器…这个灭蚊器被放在1号节点.
每个时刻,它都会工作一次,把和灭蚊器距离小于等于d 范围内的蚊子全部杀死.(d=0 时
只能控制1 号点一个位置)
遗憾的是,兔子洞尚未通电,兔子只能用爱发电. 因此,每个时刻灭蚊器只有p/q的概
率能够正常工作.如果不能正常工作,那么蚊子将不受到任何影响.
灭蚊器无法影响出现之前和消失之后的蚊子.但在蚊子出现在起点和消失在终点的
那个时刻,如果灭蚊器正常工作且蚊子在作用范围内,这只蚊子仍会被杀死.
兔子对liu_runda 的诚意表示怀疑…于是它让liu_runda 算出灭蚊器在一晚上期望
能杀死多少蚊子.liu_runda 当然会算了,但是他想考考你.
因为兔子讨厌小数,你需要输出这个期望值模109+7 后的结果.即:如果期望值可以
表示成有理数a/b(a,b 为整数),你需要输出a*b-1 mod 1000000007(109+7)的值.
(如果你算错了,就会被liu_runda 拿去喂兔子,啊呜~~)
【输入格式】
第一行一个整数n,表示兔子洞中洞穴的个数.洞穴编号为1 到n 的整数.
接下来n-1 行,每行两个整数u,v,表示u 和v 两个洞穴之间有一条通道.
接下来一行三个整数d,p,q,表示灭蚊器的作用范围是d,每个时刻工作的概率是
p/q.
【输出格式】
一行一个整数ans,表示期望模109+7 的值.
【样例输入】
3
1 2
1 3
1 1 2
【样例输出】
750000007
【样例解释】
共有2 只蚊子,一只从2 飞到3,一只从3 飞到2.灭蚊器的作用范围是1,那么
三个点都在作用范围内,每个蚊子会有三个时刻在作用范围内,那么每只蚊子生还的概率
都是1/8,经过计算,我们期望能够打死7/4 只蚊子,说明liu_runda 提供的灭蚊器是比
较靠谱的.我们输出7*4-1mod1000000007 的值750000007.
【数据范围】
记m 为叶子节点的个数.
对于第1 个测试点,n=300
对于第2,3 个测试点,n=3000
对于第4 个测试点,d=0,n=100000
对于第5 个测试点,p/q=1,n=100000
对于第6,7 个测试点n=5000000,m<=500
对于第8,9,10 个测试点,n=5000000
对于所有测点,m < n<=5000000,0<=d<=n,1<=p<=q<=109+7 保证1 号节点至少和两条通道相连.
题解
感觉是比较naive的期望, noip难度. 但是还是写错了. 存活的蚊子要好算得多, 直接最后用总的蚊子 - 期望存活的蚊子就是题目所求.
我们会发现我们只用考虑lca的贡献. 对于一个叶子节点到另一个叶子节点, 我们可以看成一个叶子节点到lca的, 和另一个到lca的, 我们称之为半路径. 这样相乘就是合法的路径, 再来算期望活着的蚊子. 我们对于每一节点用f数组来存所有这样的半路径, 那么对于u的儿子v. v到u其他子树的所有路径贡献就是(f[u] - f[v]) * f[v], 这就映射了所有路径(在dep[u]是否 小于d是要特判一下f[v]的贡献), 也就算出了所有期望存活的蚊子.
注意那个分数可以一开始就转成分子乘分母的逆元来处理.
#include<stdio.h>
const int mod = 1e9 + 7;
const int maxn = 5e6 + 5;
typedef long long dnt;
int n, num, d, pp, qq;
int h[maxn], dep[maxn], siz[maxn];
dnt f[maxn], ans, p;
struct edge{ int nxt, v;}e[maxn * 2];
inline int read(){
register int x = 0;
register char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
inline void add(int u, int v){
e[++num].v = v, e[num].nxt = h[u], h[u] = num;
e[++num].v = u, e[num].nxt = h[v], h[v] = num;
}
inline dnt mpow(dnt a, dnt b){
dnt ret = 1;
while(b){
if(b & 1) ret = ret * a % mod;
a = a * a % mod, b >>= 1;
}
return ret;
}
void dfs(int u, int fa){
for(int i = h[u]; i; i = e[i].nxt){
int v = e[i].v;
if(v == fa) continue;
dep[v] = dep[u] + 1;
dfs(v, u);
siz[u] += siz[v];
f[u] = (f[u] + f[v] * p) % mod;
}
if(!siz[u]){
siz[u] = 1;
if(dep[u] <= d) f[u] = p;
else f[u] = 1;
return;
}
if(dep[u] > d) f[u] = siz[u];
dnt delta = 0;
for(int i = h[u]; i; i = e[i].nxt){
int v = e[i].v;
if(v == fa) continue;
if(dep[u] <= d) delta = f[u] - f[v] * p;
else delta = f[u] - f[v];
delta = (delta % mod + mod) % mod;
ans = (ans + delta * f[v] % mod) % mod;
}
}
int main(){
freopen("mosquito.in", "r", stdin);
freopen("mosquito.out", "w", stdout);
n = read();
int u, v;
for(int i = 1; i < n; ++i){
u = read(), v = read();
add(u, v);
}
d = read(), pp = read(), qq = read();
p = 1ll * (qq - pp) * mpow(qq, mod - 2) % mod;
dfs(1, 1);
ans = (1ll * siz[1] * (siz[1] - 1) % mod - ans + mod) % mod;
printf("%I64d\n", ans);
return 0;
}