NOIP模拟17.8.20
A.阶乘
【题目描述】
亲爱的xyx同学正在研究数学与阶乘的关系,但是他喜欢颓废,于是他就制作了一个和阶乘有关系的数学游戏:
给出两个整数 n,m,令 t = !n,每轮游戏的流程如下
1.如果 m不能整除t ,即 t mod m ≠ 0,跳到第三步;如果能整除,跳到第二步
2.令 t = t/m,xyx的得分+1并返回第一步
3.游戏结束
xyx共进行T轮游戏,他想知道每轮他的得分是多少
【输入描述】
第一行一个整数 T,表示游戏轮数
接下来 T行,每行两个正整数n,m ,含义见题目描述
【输出描述】
共T 行,每行输出一个整数,表示xyx该轮游戏得到的分数
【样例输入】
4
2 3
3 3
23456 5
8735373 10
【样例输出】
0
1
5861
2183837
【限制与约定】
对于30% 的数据, n,m ≤ 20
对于60% 的数据, n,m ≤ 1e6
对于 100%的数据, n,m ≤ 1e9 ,T ≤ 10,1 < m <= 15
【题解】
此题极好
n! = 1*2*3*4*5*...*n
把m分解,设第i个质因数为pi,假设n中有k个pi
n! = 1*2*3..*pi*...*2pi*...*3pi*...*kpi...*n
然后我们让n/=pi,可以得到
n/pi! = 1*2*3*...*pi*...*2pi*...*3pi*...*(k - n/p - 1)pi*...*n/pi
证明比较显然,k*p每次-p就到达下一个p的倍数,于是,n每次-p,(n-p)!就
少一个p。干脆直接n不断/p就好了
最终结果要除以m中pi的数量
取一个min即可
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #define min(a, b) ((a) < (b) ? (a) : (b)) 6 7 inline void read(long long &x) 8 { 9 x = 0;char ch = getchar(), c = ch; 10 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 11 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 12 if(c == '-')x = -x; 13 } 14 15 const long long INF = 0x3f3f3f3f; 16 17 long long n,m,t,ans; 18 19 int main() 20 { 21 read(t); 22 register long long k,tmp,p; 23 for(;t;--t) 24 { 25 read(n), read(m); 26 ans = INF; 27 for(register int i = 2;i <= m;++ i) 28 { 29 if(m % i == 0) 30 { 31 k = 0, p = n, tmp = 0; 32 while(m % i == 0)m /= i, ++ k; 33 while(p)tmp += p/i, p/= i; 34 ans = min(ans, tmp/k); 35 } 36 } 37 printf("%I64d ", ans); 38 } 39 return 0; 40 }
B.妹子
【题目描述】
亲爱的xyx同学正在研究数学与妹子的关系,但是他喜欢颓废,于是他就制作了一个和妹子有关系的数学游戏:
有一棵树,树上每个点i 有一个权值ai ,每轮游戏中xyx和妹子会各自选择一个点,然后对于树上所有的点来说,如果它与
xyx所选点的距离更近,那么xyx就会得到等于该点权值的分数;如果它与妹子所选点的距离更近,那么妹子就会得到等
于该点权值的分数;如果距离相等,那么xyx和妹子都不会得到分数。
xyx想知道,每轮游戏中,他和妹子得到的分数分别是多少
两点间的距离定义为该两点最短路径所经过的边的权值之和
【输入描述】
第一行一个整数 n,表示树的顶点数。
接下来n -1行,每行两个整数 u,v,w,表示第i 条边连接点 u,v,边权为w
接下来一行共 n个整数,表示树上每个点 i的权值ai 。
接下来一行一个整数 m,表示游戏轮数
接下来 m行,每行两个整数x,y ,分别表示xyx所选的点和妹子所选的点
【输出描述】
共 m行,每行输出两个整数,表示xyx得到的分数和妹子得到的分数,中间用一个空格隔开。
【样例输入】
3
1 2 1
1 3 1
10 1 1
2
2 3
1 3
【样例输出】
1 1
11 1
【限制与约定】
对于10% 的数据, n,m ≤ 100
对于30%的数据, n,m ≤ 1000
对于另外15% 的数据,第 i条边连接点i,i+1
对于另外15% 的数据,树和询问保证随机生成
对于 100%的数据,n ≤ 100000,m ≤ 50000,1 ≤ x, y,u, v ≤ n,1 ≤ w,ai ≤ 10
【题解】
此题极好+1
考察细节,参见代码
“点的类型可以看成是单调的(属于x->谁都不属于->属于y)
设dis[x]表示x到根的距离,每次询问中dis[x]>dis[y],他们的lca为p
那么点的类型边界一定在p->x的路径上
由于在链上存在单调性,可以二分+倍增找深度差为k的父亲来做
复杂度O(n log^2 n)
也可以倍增+从高到低确定二进制位,这一位是1时如果已经不受x控制,则这一位一定为0;反之这一位为0(贪心思路)
复杂度O(n log n)”
——张浩南
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 6 inline void read(long long &x) 7 { 8 x = 0;char ch = getchar(),c = ch; 9 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 10 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 11 if(c == '-')x = -x; 12 } 13 14 const long long MAXN = 200000 + 10; 15 16 struct Edge 17 { 18 long long u,v,next,w; 19 Edge(long long _u, long long _v, long long _next, long long _w){u = _u;v = _v;next = _next;w = _w;} 20 Edge(){} 21 }edge[MAXN << 1]; 22 23 long long n,m,b[MAXN],value[MAXN],sum[MAXN],path[MAXN],p[25][MAXN],deep[MAXN],fa[MAXN],head[MAXN],cnt; 24 25 inline void insert(long long a, long long b, long long c) 26 { 27 edge[++cnt] = Edge(a,b,head[a],c); 28 head[a] = cnt; 29 } 30 31 void dfs(long long u) 32 { 33 b[u] = 1; 34 sum[u] = value[u]; 35 for(register long long pos = head[u];pos;pos = edge[pos].next) 36 { 37 long long v = edge[pos].v; 38 if(b[v])continue; 39 path[v] = path[u] + edge[pos].w; 40 deep[v] = deep[u] + 1; 41 dfs(v); 42 fa[v] = u; 43 p[0][v] = u; 44 sum[u] += sum[v]; 45 } 46 } 47 48 //p[0]是点 p[1]是路径长度 49 inline void yuchuli() 50 { 51 long long M = 0; 52 while((1 << M) <= n)++ M; 53 -- M; 54 for(register long long i = 1;i <= M;++ i) 55 for(register long long j = 1;j <= n;++ j) 56 p[i][j] = p[i - 1][p[i - 1][j]]; 57 } 58 59 long long lca(long long va, long long &vb) 60 { 61 int flag = 0; 62 if(deep[va] < deep[vb]) 63 { 64 long long tmp = va; 65 va = vb; 66 vb = tmp; 67 flag = 1; 68 } 69 long long M = 1; 70 while((1 << M) + deep[vb] <= deep[va])++ M; 71 -- M; 72 for(register long long i = M;i >= 0;-- i) 73 if((1 << i) + deep[vb] <= deep[va]) 74 va = p[i][va]; 75 if(va == vb)return va; 76 M = 1; 77 while((1 << M) + deep[vb] <= n)++ M; 78 -- M; 79 for(register long long i = M;i >= 0;-- i) 80 if(p[i][va] != p[i][vb]) 81 { 82 va = p[i][va]; 83 vb = p[i][vb]; 84 } 85 if(flag)vb = va; 86 return p[0][va]; 87 } 88 89 int main() 90 { 91 read(n); 92 register long long tmp1, tmp2, tmp3; 93 for(register long long i = 1;i < n;++ i) 94 { 95 read(tmp1),read(tmp2),read(tmp3); 96 insert(tmp1, tmp2, tmp3); 97 insert(tmp2, tmp1, tmp3); 98 } 99 for(register long long i = 1;i <= n;++ i) read(value[i]); 100 deep[1] = 0; 101 dfs(1); 102 yuchuli(); 103 read(m); 104 register long long a,b,ans1,ans2,lenth,sa,sb,c,ok,M = 0; 105 while((1 << M) <= n)++ M; 106 -- M; 107 for(register long long i = 1;i <= m;++ i) 108 { 109 read(a),read(b); 110 if(a == b) 111 { 112 printf("0 0 "); 113 continue; 114 } 115 ok = 0; 116 if(path[a] < path[b]) 117 { 118 int tmp = a; 119 a = b; 120 b = tmp; 121 ok = 1; 122 } 123 sa = a, sb = b; 124 c = lca(sa, sb); 125 ans1 = ans2 = 0; 126 /* if(c != 0)*/ 127 lenth = path[a] + path[b] - (path[c] << 1); 128 /* else 129 lenth = path[a] - path[b];*/ 130 for(register int i = M;i >= 0;-- i) 131 if(((path[a] - path[p[i][sa]]) * 2) < lenth) 132 sa = p[i][sa]; 133 if(((path[a] - path[p[0][sa]]) << 1) == lenth) 134 { 135 if(p[0][sa] == c) 136 { 137 if(ok) 138 printf("%lld %lld ", sum[sb], sum[sa]); 139 else 140 printf("%lld %lld ", sum[sa], sum[sb]); 141 continue; 142 } 143 else 144 { 145 if(ok) 146 printf("%lld %lld ", sum[1] - sum[fa[sa]], sum[sa]); 147 else 148 printf("%lld %lld ", sum[sa], sum[1] - sum[fa[sa]]); 149 continue; 150 } 151 } 152 else 153 { 154 if(ok) 155 printf("%lld %lld ", sum[1] - sum[sa], sum[sa]); 156 else 157 printf("%lld %lld ", sum[sa], sum[1] - sum[sa]); 158 continue; 159 } 160 } 161 return 0; 162 }
这题我调了很久,最后发现我本以为最不可能错的LCA居然写挂了,其他地方都没错。。。
C.在
【题目描述】
亲爱的xyx同学正在研究数学与存在的关系,但是他喜欢颓废,于是他就制作了一个和存在有关系的数学游戏:
有一个集合,游戏开始时里面有一些数,xyx每次可以选择集合中的两个数(这两个数可以是同一个数)相减,然后把它
们差的绝对值放入集合中(如果集合中已存在该数则不放入),经过任意次这样的操作后,xyx会给出一个数字 k,问它
是否能存在在集合中,如果能,那么游戏成功,反之失败。
(换句话说,xyx想知道这样不断操作下去,是否能凑出数字 k)
xyx有一个数列 a,他每次会给出三个数 l,r,x,即把所有的ai(l ≤ i ≤ r) 放入集合中,并且想要知道当 k = x时游戏是否成功
有多组询问且询问之间相互独立,你可以认为每次询问后xyx就把游戏中的集合清空了
【输入描述】
第一行两个整数n 和 m,分别表示数字集合的大小和询问次数。
第二行共 n个整数,表示数列 a,第 i个数是 ai。
接下来 m行,每行三个整数 l,r,x,含义见题目描述
【输出描述】
共 m行,每行一个字符串,表示对应询问的答案,如果游戏成功输出”win”,反之输出”lose”。(不输出引号)
【样例输入】
7 5
3 6 4 2 7 1 8
1 2 3
3 7 0
3 4 2
3 4 5
2 3 3
【样例输出】
win
win
win
lose
lose
【限制与约定】
对于20% 的数据, ai,x ∈ [0,1]
对于另外20% 的数据,保证 ai 随机生成
对于另外 20%的数据, l = 1
对于100% 的数据, n,m ≤ 100000,0 ≤ x,ai ≤ 10 ,1 ≤ l ≤ r ≤ n
【题解】
此题一般
当时场外试了几组数据,然后就看出来了
其实能不能凑出k,取决于两个条件:
1、数列中存在大于等于k的数
2、数列的gcd为k的因数
很显然,数列每个数都可以写作p*gcd的性质,他们加减,仍是p*gcd
每次写st表都要手推。。。所以注释里保存了我手推的过程,便于大家学习。。。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #define max(a, b) ((a) > (b) ? (a) : (b)) 6 7 inline void read(int &x) 8 { 9 x = 0;char ch = getchar(),c = ch; 10 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 11 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 12 if(c == '-')x = -x; 13 } 14 15 const int MAXN = 100000 + 10; 16 17 18 /* 19 data[i][j]表示区间[j, j + 2^i - 1] 20 data[i][j] = gcd(data[i - 1][j], data[i - 1][j + 2^(i - 1)] 21 [j, j + 2^(i - 1) - 1] [j + 2 ^ (i - 1), j + 2^ i - 1] 22 23 [l,r] 24 2 ^ M <= r - l + 1 < 2 ^ (M + 1) 25 data[M][l], data[M][r - 2^M + 1] 26 */ 27 int n,m,num[MAXN],data[2][22][MAXN],lo[MAXN],pow2[30]; 28 29 inline int gcd(int a, int b) 30 { 31 return b == 0 ? a : gcd(b, a % b); 32 } 33 34 inline void yuchuli() 35 { 36 int M = lo[n]; 37 for(register int i = 1;i <= M;++ i) 38 for(register int j = 1;j <= n;++ j) 39 { 40 data[1][i][j] = gcd(data[1][i - 1][j], data[1][i - 1][j + pow2[i - 1]]); 41 data[0][i][j] = max(data[0][i - 1][j], data[0][i - 1][j + pow2[i - 1]]); 42 } 43 } 44 45 int findma(int l, int r) 46 { 47 return max(data[0][lo[r - l + 1]][l], data[0][lo[r - l + 1]][r - pow2[lo[r - l + 1]] + 1]); 48 } 49 50 int findgcd(int l, int r) 51 { 52 return gcd(data[1][lo[r - l + 1]][l], data[1][lo[r - l + 1]][r - pow2[lo[r - l + 1]] + 1]); 53 } 54 55 int main() 56 { 57 read(n);read(m); 58 lo[2] = 1; 59 for(register int i = 3;i <= n;++ i)lo[i] = lo[i >> 1] + 1; 60 pow2[0] = 1; 61 for(register int i = 1;i <= 25;++ i)pow2[i] = pow2[i - 1] << 1; 62 for(register int i = 1;i <= n;++ i)read(num[i]), data[0][0][i] = data[1][0][i] = num[i]; 63 yuchuli(); 64 register int l,r,k; 65 for(register int i = 1;i <= m;++ i) 66 { 67 read(l),read(r),read(k); 68 int ma = findma(l, r); 69 if(ma < k) 70 { 71 printf("lose "); 72 continue; 73 } 74 int g = findgcd(l, r); 75 if(g == 0) printf("win "); 76 else if(k % g == 0) 77 { 78 printf("win "); 79 continue; 80 } 81 else 82 { 83 printf("lose "); 84 continue; 85 } 86 } 87 return 0; 88 }