裂项∑ i = 1 n ( a i − a i + k ) = ∑ i = 1 k a i − ∑ i = n + 1 n + k a i \sum_{i=1}^{n}(a_{i}-a_{i+k})=\sum_{i=1}^{k}a_{i}-\sum_{i=n+1}^{n+k}a_{i} i = 1 ∑ n ( a i − a i + k ) = i = 1 ∑ k a i − i = n + 1 ∑ n + k a i
约瑟夫环问题约瑟夫环问题
设J n , m J_{n, m} J n , m 表示规模分别为n , m n, m n , m 的约瑟夫问题的答案。我们有如下递归式
J n , m = ( J n − 1 , m + m ) m o d n J_{n, m}=\left(J_{n-1, m}+m\right) \bmod n J n , m = ( J n − 1 , m + m ) m o d n
这个也很好推。从 0 开始数m m m 个,让第m − 1 m-1 m − 1 个人出局后剩下n − 1 n-1 n − 1 个人,计算出在n − 1 n-1 n − 1 个人中选的答案后,再加一个相对位移m m m 得到真正的答案。
题目说是从k k k 开始报数,所以答案再加上k k k 。
时间复杂度Θ ( n ) \Theta(n) Θ ( n ) :
1 2 3 4 5 int res = 0 ;for (int i = 2 ; i <= n; ++i) { res = (res + m) % i; } cout << ((res + k - 1 ) % n + 1 ) << '\n' ;
时间复杂度Θ ( k log n ) \Theta(k\log n) Θ ( k log n ) :
1 2 3 4 5 6 7 8 9 10 int josephus (int n, int k) { if (n == 1 ) return 0 ; if (k == 1 ) return n - 1 ; if (k > n) return (josephus (n - 1 , k) + k) % n; int res = josephus (n - n / k, k); res -= n % k; if (res < 0 ) res += n; else res += res / (k - 1 ); return res; }
2024 CCPC 济南 D. Hero of the Kingdom1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void eachT () { ll p = read (), a = read (), b = read (); ll q = read (), c = read (), d = read (); ll m = read (), t = read (); ll res = 0 ; int cnt = 0 ; while (t >= b + d) { ll x = m / p; if (x == 0 ) break ; ll lun = min (((x + 1 ) * p - m + ((q - p) * x) - 1 ) / ((q - p) * x), t / ((a + c) * x + (b + d))); if (lun == 0 ) { x = min (x, (t - (b + d)) / (a + c)); lun = 1 ; } if (x == 0 ) break ; m += lun * ((q - p) * x); t -= lun * ((a + c) * x + (b + d)); } printf ("%lld\n" , m); }
2024 杭电多校 9005 - 怪物猎人[[…/contests/2024 杭电多校 /2024-08-16:2024“钉耙编程”中国大学生算法设计超级联赛(9)|2024-08-16:2024“钉耙编程”中国大学生算法设计超级联赛(9)]]
赛时代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void eachT () { ll k = read (), x = read (), y = read (); ll mx = max (x, y); ll mn = min (x, y); if (x == y) { if (((k + x - 1 ) / x) & 1 ) { printf ("Yes\nNo\n" ); } else { printf ("No\nYes\n" ); } return ; } ll m = k / mx; if (m * mx == k) { m--; } if ((m + 1 ) * mn >= k) { if (m & 1 ) { printf ("No\nYes\n" ); } else { printf ("Yes\nNo\n" ); } return ; } printf ("Yes\nYes\n" ); }
优化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void eachT () { ll k = read (), x = read (), y = read (); if (x > y) swap (x, y); ll m = (k + y - 1 ) / y; if (m * x >= k) { if (m & 1 ) { printf ("Yes\nNo\n" ); } else { printf ("No\nYes\n" ); } } else { printf ("Yes\nYes\n" ); } }
2024 杭电多校 9007 - 小猫钓鱼[[…/contests/2024 杭电多校 /2024-08-16:2024“钉耙编程”中国大学生算法设计超级联赛(9)|2024-08-16:2024“钉耙编程”中国大学生算法设计超级联赛(9)]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 signed main () { int t; cin >> t; while (t--) { int n; cin >> n; set<int >a; int tmp; for (int i = 1 ; i <= n; i++) { cin >> tmp; a.insert (tmp); } for (int i = 1 ; i <= n; i++) { cin >> tmp; } int pr = n - a.size (); int only = a.size () - pr; if (pr) { cout << "shuishui" << endl; } else { cout << "sha7dow" << endl; } } return 0 ; }
2024 杭电多校 8007 - cats 的 k-xor已知在k k k 进制下a xor b = c a\operatorname{xor} b=c a x o r b = c ,其中xor \operatorname{xor} x o r 定义为不进位加法。求可能k k k 的数量。([[…/contests/2024 杭电多校 /2024-08-12:2024“钉耙编程”中国大学生算法设计超级联赛(8)|2024-08-12:2024“钉耙编程”中国大学生算法设计超级联赛(8)]])
思路 k ∣ a + b − c k\mid a+b-c k ∣ a + b − c 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ll a = read (), b = read (), c = read (); ll v = a + b - c; if (v < 0 ) { printf ("0\n" ); } else if (v == 0 ) { printf ("-1\n" ); } else { auto solve = [&](ll x) { ll A = a, B = b, C = c; while (A || B || C) { if ((A % x + B % x) % x != C % x) { return 0 ; } A /= x, B /= x, C /= x; } return 1 ; }; int ans = 0 ; for (ll i = 2 ; i * i <= v; ++i) { if (v % i) continue ; ans += solve (i); if (i * i != v) ans += solve (v / i); } if (v > 1 ) ans += solve (v); printf ("%d\n" , ans); }
2024 牛客多校 3A - Bridging the Gap 2[[…/contests/2024 牛客多校 /2024-07-23:2024 牛客暑期多校训练营 3#A - Bridging the Gap 2|2024-07-23:2024 牛客暑期多校训练营 3]]
题意 n n n 个人在河的一侧,第i i i 个人有体力值h i h_{i} h i 有且仅有一艘能容纳L L L 到R R R 个人的船,问是否能将所有人从河的左侧运送到右侧。
解 最后一趟一定运R R R 个人最优,前面几轮尽可能地R R R 人去,L L L 人回,每次净运R − L R-L R − L 个人。能运满的来回数t = ⌊ n − R R − L ⌋ t=\left\lfloor \frac{n-R}{R-L} \right\rfloor t = ⌊ R − L n − R ⌋ ,可能会多出一趟,不到R R R 人去,但L L L 人回,这个判断称作最后一个来回f f f 。
前t t t 个来回,每个来回消耗体力R + L R+L R + L ,最后一个来回,去n − R − t ( R − L ) + L n - R - t(R - L)+L n − R − t ( R − L ) + L 人,回L L L 人,消耗体力n − R − t ( R − L ) + 2 L n - R - t(R - L)+2L n − R − t ( R − L ) + 2 L ,最后一趟消耗体力R R R ,这样就得到了总的需要消耗的体力值。
再看能消耗的体力值。设总轮数l = 2 ( t + f ) + 1 l=2(t+f)+1 l = 2 ( t + f ) + 1 ,首先如果体力值h i ⩾ l h_{i} \geqslant l h i ⩾ l ,置h i ← l h_{i} \leftarrow l h i ← l 。每个人只会跑奇数趟,置h i h_{i} h i 向下取为奇数,现在能消耗的体力值即是∑ h i \sum h_{i} ∑ h i 。能够证明总是能够消耗尽的。
只需判断是否满足需要消耗的体力值⩾ \geqslant ⩾ 能消耗的体力值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ll n = read (), L = read (), R = read (); int flag = 0 ;ll sum = 0 ; ll t1 = (n - R) / (R - L); if ((n - R) % (R - L)) { flag = 1 ; } ll lun = 2 * (t1 + flag) + 1 ; for (int i = 1 ; i <= n; i++) { int x = read (); if (!(x & 1 )) x -= 1 ; if (x > lun) x = lun; sum += x; } ll lhs = 0 ; if (flag) lhs = (n - R - t1 * (R - L)) + 2 * L;lhs += t1 * (R + L) + R; cout << (lhs <= sum ? "Yes" : "No" ) << "\n" ;
2024 牛客多校 7I. Fight Against the Monster题意 需要造出若干台战争机器对抗怪兽。每台机器有战斗和创造功能:
战斗:使怪兽血量减 1,然后失去所有功能; 创造:m m m 台机器同时使用,在这之后会产生k ( k ⩽ m ) k\ (k \leqslant m) k ( k ⩽ m ) 台新的战争机器,原先的m m m 台机器失去创造功能。 怪兽的初始血量为h h h ,其血量不超过0 0 0 时就会死亡。求最初至少需要多少台战争机器才能打败怪兽。
称能继续创造的机器是有用的,每次创造操作相当于减少了m − k m-k m − k 台有用的机器,同时打出m m m 点伤害,最后一次创造剩下的k k k 台机器,还可以打k k k 点伤害。设总共创造了x x x 次恰好打败怪物,则m ( x + 1 ) + k ⩾ h m(x+1)+k \geqslant h m ( x + 1 ) + k ⩾ h 。最初需要的机器数是m + x ( m − k ) m+x(m-k) m + x ( m − k ) 。另一种情形是只创造x − 1 x-1 x − 1 次,设m x + k + r = h mx+k+r=h m x + k + r = h ,最初需要的机器数是m + ( x − 1 ) ( m − k ) + r m+(x-1)(m-k)+r m + ( x − 1 ) ( m − k ) + r 。
1 2 3 4 5 6 7 int m, k, h;cin >> m >> k >> h; int x = (h - k) / m;int del = h - k - x * m;int ans1 = m + (x - 1 ) * (m - k) + del;int ans2 = m + x * (m - k);cout << (h == 0 ? 0 : min (ans1, ans2)) << endl;
2024 牛客多校 - 10H. All-in at the Pre-flop[[…/contests/2024 牛客多校 /2024-08-15:2024 牛客暑期多校训练营 10|2024-08-15:2024 牛客暑期多校训练营 10]]
题意 两个玩家在一场公平的游戏里互相全押。当赢家筹码不少于输家时,游戏立即结束;否则输家向赢家支付等同于赢家筹码的筹码。问两个玩家的胜率。
思路 注意到a a + b \cfrac{a}{a+b} a + b a 的二进制表示第i i i 位是1 1 1 当且仅当第i i i 轮a > b a>b a > b 。答案为a a + b , b a + b \cfrac{a}{a+b},\cfrac{b}{a+b} a + b a , a + b b 。
题意 有一个长度为n n n 的一维网格。网格的第i i i 个单元格包含字符s i s_i s i ,是 <
或 >
。当弹球放在其中一个格子上时,它会按照以下规则移动:
如果弹球位于第i i i 个格子上且s i s_i s i 为 <
,则弹球在下一秒会向左移动一个单元格; 如果s i s_i s i 为 >
,则向右移动一个单元格; 弹球移动后,字符s i s_i s i 会反转。 对于每个i i i ,求弹球放置在第i i i 格时离开网格的时长。
思路 设起始向左走,且从左边出,答案为
( i − l 1 ) + ( r 1 − l 1 ) + ( r 1 − l 2 ) + ⋯ + ( r x − l x ) + ( r x − 0 ) = i + ∑ j = 1 x ( r j − l j ) (i-l_{1})+(r_{1}-l_{1})+(r_{1}-l_{2})+\dots+(r_{x}-l_{x})+(r_{x}-0)=i+\sum_{j=1}^{x}(r_{j}-l_{j}) ( i − l 1 ) + ( r 1 − l 1 ) + ( r 1 − l 2 ) + ⋯ + ( r x − l x ) + ( r x − 0 ) = i + j = 1 ∑ x ( r j − l j )
式中x = min { l , r } x=\min\lbrace l,r \rbrace x = min { l , r } 是左右侧会使得弹球反向移动的字符个数的较小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 int n;string s; cin >> n >> s; vector<int > a (n + 1 ) ;for (int i = 1 ; i <= n; i++) { if (s[i] == '<' ) a[i] = 1 ; else a[i] = -1 ; } vector<array<int , 2>> cnt (n + 1 ); vector<ll> l, r; l.push_back (0 ), r.push_back (0 ); for (int i = 1 ; i <= n; i++) { cnt[i] = cnt[i - 1 ]; if (s[i] == '<' ) { cnt[i][0 ]++; l.push_back (i); } else { cnt[i][1 ]++; r.push_back (i); } } auto prel = l, prer = r;for (int i = 1 ; i < l.size (); i++) prel[i] += prel[i - 1 ];for (int i = 1 ; i < r.size (); i++) prer[i] += prer[i - 1 ];vector<ll> ans (n + 1 ) ;for (int i = 1 ; i < prel.size (); i++) { ll suml = prel[i] - prel[cnt[i][0 ]]; ll sumr = prer[cnt[i - 1 ][1 ]] - 0 ; ans[i] = (suml - sumr) * 2 + a[i] * i; } for (int i = prel.size (); i <= n; i++) { ll suml = prel.back () - prel[cnt[i][0 ]]; ll sumr = prer[cnt[i - 1 ][1 ]] - prer[i - prel.size ()]; ans[i] = n + 1 + (suml - sumr) * 2 + a[i] * i; } for (int i = 1 ; i <= n; i++) { cout << ans[i] << " \n" [i == n]; }
题意 象棋中两个兵,谁会吃掉谁?
思路 考虑极左点和极右点。
1 2 3 4 5 6 7 8 9 10 11 12 13 scanf ("%d %d %d %d %d %d" , &h, &w, &x1, &y1, &x2, &y2);if (x1 >= x2) { printf ("Draw\n" );} else if (x2 - x1 & 1 ) { ll l1 = max (y1 - (x2-x1)/2 , 1 ), r1 = min (y1 + (x2-x1)/2 , w); ll l2 = max (y2 - (x2-x1)/2 , 1 ), r2 = min (y2 + (x2-x1)/2 , w); if (l1 <= l2 + 1 && r1 >= r2 - 1 ) printf ("Alice\n" ); else printf ("Draw\n" ); } else { ll l1 = max (y1 - (x2-x1)/2 , 1 ), r1 = min (y1 + (x2-x1)/2 , w); ll l2 = max (y2 - (x2-x1-1 )/2 , 1 ), r2 = min (y2 + (x2-x1-1 )/2 , w); if (l2 <= l1 + 1 && r2 >= r1 - 1 ) printf ("Bob\n" ); else printf ("Draw\n" ); }
题意 求在半径为r r r ,圆心为原点的圆外的到原点的(欧几里得)距离最小的点.
思路 设( x , y ) (x,y) ( x , y ) ,即是求满足d 2 = x 2 + y 2 > r 2 d^2=x^2+y^2>r^2 d 2 = x 2 + y 2 > r 2 的最小的d d d .一方面,上式等价于d 2 ⩾ r 2 + 1 d^2\geqslant r^2+1 d 2 ⩾ r 2 + 1 ;另一方面,取( 1 , r ) (1,r) ( 1 , r ) ,d 2 = r 2 + 1 d^2=r^2+1 d 2 = r 2 + 1 .因此d 2 d^2 d 2 的最小值是r 2 + 1 r^2+1 r 2 + 1 ,点的坐标可取( 1 , r ) (1,r) ( 1 , r ) .
题意 在平面上的放置n n n 个整点,任意整点的距离大于1 1 1 ,求它们的直角坐标的最大值的最小值.
思路 ⌈ n ⌉ − 1 = ⌊ n − 1 ⌋ \lceil\sqrt{n}\rceil-1=\big\lfloor\sqrt{n-1}\big\rfloor ⌈ n ⌉ − 1 = ⌊ n − 1 ⌋ .
注意 直接使用 sqrt()
函数有精度问题,需要手写一个.
评注 乱搞是一种做 CF 思维题的好方法!
在第四种数据中,输出结果为987654321 987654321 9 8 7 6 5 4 3 2 1 ,非常巧妙的数字.说明什么?出题人可能是先出了输出数据,再据此反推出了输入的975461057789971042 975461057789971042 9 7 5 4 6 1 0 5 7 7 8 9 9 7 1 0 4 2 .于是,这道题很有可能存在一个Θ ( 1 ) \Theta(1) Θ ( 1 ) 的通项公式,可以快速算出结果,并根据答案反推输入.
打开计算器,很容易发现:98765432 1 2 + 1 = 975461057789971042 987654321^2+1=975461057789971042 9 8 7 6 5 4 3 2 1 2 + 1 = 9 7 5 4 6 1 0 5 7 7 8 9 9 7 1 0 4 2 ,即答案为n − 1 \sqrt{n-1} n − 1 .
或者:⌊ 975461057789971042 ⌋ = 987654321 \lfloor\sqrt{975461057789971042}\rfloor=987654321 ⌊ 9 7 5 4 6 1 0 5 7 7 8 9 9 7 1 0 4 2 ⌋ = 9 8 7 6 5 4 3 2 1 ,即答案为⌊ n ⌋ \lfloor\sqrt{n}\rfloor ⌊ n ⌋ .
又或者:⌈ 975461057789971042 ⌉ − 1 = 987654321 \lceil\sqrt{975461057789971042}\rceil-1=987654321 ⌈ 9 7 5 4 6 1 0 5 7 7 8 9 9 7 1 0 4 2 ⌉ − 1 = 9 8 7 6 5 4 3 2 1 ,即答案为⌈ n ⌉ − 1 \lceil\sqrt{n}\rceil-1 ⌈ n ⌉ − 1 .
显然,第一式不一定能得到整数解,由题意知答案为整数,所以是错的.把其他三组数据带进第二式,发现也有误.而第三式是都符合的.于是你可以快乐地A C \rm AC A C 这道题.
事实上,经过推导,正确答案也是⌈ n ⌉ − 1 \lceil\sqrt{n}\rceil-1 ⌈ n ⌉ − 1 .
题意 一个抢红包系统的规则:假如现在有w w w 元,那么能抢到的钱就是[ 0 , w ] [0,w] [ 0 , w ] 等概率均匀随机出的一个实数x x x .现在红包发了一个w w w 元的红包,有n n n 个人来抢,问第k k k 个人期望抢到多少钱?对1 0 9 + 7 10^9+7 1 0 9 + 7 取模.思路 答案为w 2 k m o d 1 0 9 + 7 \displaystyle\frac{w}{2^k}\bmod10^9+7 2 k w m o d 1 0 9 + 7 .题意 将尺寸为k × 1 k \times 1 k × 1 或1 × k ( k ≥ 2 ) 1 \times k~(k \geq 2) 1 × k ( k ≥ 2 ) 的条状砖块填满尺寸为n × m n \times m n × m 的砖墙,求所有砖块的水平长度与垂直长度之差之和的最大值.思路 n ⋅ ⌊ m 2 ⌋ \displaystyle n\cdot\lfloor\frac m2\rfloor n ⋅ ⌊ 2 m ⌋ .