Gravatar
ychyyx
积分:278
提交:51 / 130

首先:暴力!40分

 略,大家都会

但是 暴力的另一个用处:打表!

 为什么打表呢?

 因为$2≤n,k≤10^{12}$。

 所以时间是O(1)。

 要找数学规律。

 打表:(输出的是没有被淘汰的人)


6 2  

1 2 3 4 5 6  

2 4 6  

4



8 3

1 2 3 4 5 6 7 8

2 3 5 6 8

3 5 8

5 8

8


注意到:每次剩下人中第一个人是有规律的,而且到最后一定只剩一个人(也是当前剩余人中的第一个),更重要的是,这个人的编号就是我们求的答案!!! 


设每次的第一个人依次是$x_1$,$x_2$,$x_3$,......,$x_m$。m是轮数。

则有$x_i+\lceil \frac{x_i}{k-1} \rceil=x_{i+1}$。

(这个需要自己找)

于是初始$x_1=1$ ,然后往后找。



#include<iostream>

using namespace std;

long long ans,n,k;//ans就是x

int main(){

   scanf("%lld%lld",&n,&k);

   ans=k;//前k个是依次作开头的,所以从k开始也一样

   while(ans+(ans+k-2)/(k-1)<=n){

       ans+=(ans+k-2)/(k-1);//套公式

   }

   printf("%lld",ans);//最后的开头就是最后一个人

   return 0;

}


嗯。于是就过关了!

But,洛谷上有个大佬出了个Hack,几乎把当时所有AC干成UAC了,这份代码也不例外。

就是这个  


1000000000000 10000000000



999999999907


会超时。  

分块优化:

于是我们继续想:每一轮都是一个人一个人淘汰的,所以我们对这一点进行优化…… 

因为$x_i+\lceil \frac{x_i}{k-1} \rceil=x_{i+1}$ ,所以按$k-1$分块($k-1$分母,按$x_i$与$k-1$的关系分块,这样每个块的跳跃次数都是一样的),批量跳跃被淘汰数。  


将整个数域$[1,n]$按每块$k−1$个数划分成若干块:  

第 $1$ 块:$[1,k−1]$  

第 $2$ 块:$[k,2(k−1)]$  

第 $id$ 块:$[(id−1)(k−1)+1,id(k−1)]$  

同一区块内的被淘汰数,递推的步长$\lceil \frac{x_i}{k-1} \rceil$是固定的(等于块编号 $id$),因此可以批量计算该块内的所有被淘汰数,一次跳跃,无需逐个计算。


在第$id$块中,$\lceil \frac{x_i}{k-1} \rceil$($x$ 属于第 $id$ 块),因此递推式简化为:$x_{next}=x+id$

这意味着在第 $id$ 块中,被淘汰数是以$id$为步长递增的,可以直接计算出该块内最后一个被淘汰数,一次跳到下一块,大幅减少计算次数。


正片开始

#include<iostream>

#include<cstdio>

#include<cmath>

#define ll long long

using namespace std;

ll n,k;

ll x=1,last_val;//x为当前待检查的被淘汰数,初始为第一个被淘汰数x1=1

//last_val是当前块中最后被淘汰的数(答案),不断更新,最终输出

int main(){

   scanf("%lld%lld",&n,&k);

   while(x<=n){

       ll id=(x-1)/(k-1)+1;//是当前x所在块的编号

       ll end=min(n,(k-1)*id);//当前块的末尾

       x+=((end-x)/id+1)*id;//直接跳到当前块的末尾或跳出当前块

       last_val=x-id;//计算当前块最后一个淘汰的数,也可能是所有之中的最后一个淘汰的数(即答案)

   }

   printf("%lld",last_val);

   return 0;

}


OK



题目4322  [常州市赛 2025] 金币      评论
2026-02-28 14:42:32    
Gravatar
终焉折枝
积分:1684
提交:221 / 391

更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19651912


大意

现在有多堆石子,其中第 $k$ 堆石子有 $p_k$ 个,先后手轮流操作。取石子时,可以选任意一堆进行操作。若记 $i$ 为在这次取之前这堆石子的个数,$j$ 为这次要取的石子数,$R$ 为给定的常数,则需满足以下条件:

$$1 \leq i + j \leq R,i \geq j \geq 1$$

使对方无法操作者即为胜者。


思路

我们首先考虑这个玩意的 $SG$ 函数,太坑了,你们可以手动的模一下。

我们来假设 $R = 5$ 的情况,首先 $SG(0) = 0$,$SG(1) = 1$,$SG(2) = 2$,但是我们发现,当 $i = 3$ 的时候,能取的只有 $1, 2$,取这两坨只会把必胜态留给对面,那这个时候的 $SG(3) = 0$,那再看 $SG(4) = 1$,然后 $SG(5) = 0$,当 $i \ge R$ 的时候 $SG(i) = 0$,这是显然的。

我们对于这个继续看其规律,不难通过注意或者推理得知,当且仅当 $i \le \lfloor \frac{R}{2} \rfloor$ 的时候,$SG(i) = i$,实际上就是简单的 Nim 博弈,但是一旦到达 $\lfloor \frac{R}{2} \rfloor + 1$,则 $SG(\lfloor \frac{R}{2} \rfloor + 1) = 0$,这是巧合吗?显然不是,但是只有这一个转折点吗?

我赛时最初的思路是 $SG(i) = i \pmod {\lfloor \frac{R}{2} \rfloor + 1}$,可是真的对吗?不难发现当 $R = 100$ 时,转折点在 $\lfloor \frac{R}{2} \rfloor + 1 = 51$,但是当 $i = 76$ 的时候呢?我们只能取 $24$,也就是只能取到 $[52, 75]$ 这个区间,而这个区间的 $SG$ 是 ${1, 2, 3, \cdots 24}$,所以 $SG(76) = 0$,所以我刚刚的结论是错误的,继续向后模拟,发现在 $SG(89) = 0$,不难发现,什么时候等于 $0$ 呢,我们设 $pos$ 表示上次的位置,那么有:

$$nxt = \lfloor \frac{pos + R}{2} \rfloor + 1$$

这个说明了,我们的 $SG$ 是一段一段的,最多有 $\log$ 段,故我们可以直接向后跳,提前预处理所有段,在查询的时候直接二分所在段,$\mathcal{O}(1)$ 处理询问即可。


代码


#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

#define ll long long
const int MAXN = 64;
ll R, lst[MAXN], val[MAXN];
int cnt;

inline ll Xor(ll n){
	switch(n % 4){
		case 1:
			return n - 1;
		case 2:
			return 1;
		case 3:
			return n;
		default:
			return 0;
	}
}

void init(ll x){
	cnt = 0;
	lst[0] = 0;
	ll now = x / 2 + 1;
	val[0] = Xor(now);
	while(now < x){
		lst[++ cnt] = now;
		ll nxt = (now + x) / 2 + 1;
		val[cnt] = val[cnt - 1] ^ Xor(nxt - now);
		now = nxt;
	}
}

ll ask(ll p){
	int idx = upper_bound(lst, lst + cnt + 1, p) - lst - 1;
	return (idx ? val[idx - 1] : 0) ^ Xor(p - lst[idx] + 1);
}

int main(){
	// freopen("orange.in", "r", stdin);
	// freopen("orange.out", "w", stdout);
	cin.tie(0) -> ios::sync_with_stdio(0);
	int T; cin >> T;
	while(T --){
		string op; cin >> op;
		if(op == "change"){
			cin >> R; init(R);
		}
        else{
			int n; cin >> n;
			ll ans = 0;
			while(n --){
				ll l, r; cin >> l >> r;
				ans ^= ask(r) ^ ask(l - 1);
			}
			cout << (ans ? "1" : "0");
		}
	}
	return 0;
}



题目4321  这是一道橙题      2      评论
2026-02-28 13:32:57    
Gravatar
终焉折枝
积分:1684
提交:221 / 391

更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19648636


大意

区间加,求 $a[l]^{a[l+1]^{a[l+2]^{\dots ^{a[r]}}}} \mod p$ 的值。


思路

对于这个指数塔,非常逆天,我们首先想到的肯定的是与降幂有关的内容,也就只有欧拉定理了,由于 $p$ 并非是质数,我们需要用扩展欧拉定理,那么什么是欧拉定理和扩展欧拉呢?

$\varphi(n)$ 就表示小于等于 $n$ 与 $n$ 互质的数的个数,$\varphi(1) = 1, \varphi(p) = p - 1$,这里的 $p$ 是质数。那么 $\phi(n)$ 应该怎么算呢?我们由素数分解定理,就可以有这样的公式:

$$n \prod_{p | n} (1 - \frac{1}{p})$$

这个过程我们是可以用线性筛预处理这个 $\varphi(n)$ 的。

好的,预处理讲完了,接下来讲欧拉定理,当 $\gcd(a, m) = 1$,那么 $a ^ {\varphi(m)} \equiv 1 \pmod m$,这个在模数为质数的时候就可以快速降幂,但是要是不是质数呢?没事,我们有扩展欧拉定理,其内容如下:

$$a ^ b \equiv \begin{cases} a ^ {b \pmod{\varphi(m)}} & \gcd(a, m) = 1 \\ a ^ b & \gcd(a, m) \neq 1, b < \varphi(m) \\ a ^ {b \pmod{\varphi(m)} + \varphi(m)} & \gcd(a, m) \neq 1, b \ge \varphi(m) \end{cases} \pmod{m}$$

好了,知道了这些,我们回到这个题目,对于这个指数塔,其指数一直取 $\varphi$ 显然最后会变成 $1$,那么要取多少次呢?我们想想对于 $\varphi(2 ^ k)$ 来说,其等于 $2 ^ {k - 1}$,对于质数是 $p - 1$,其如果是质数就减去一变成偶数,然后就会很快变小。

那么我们这个题直接写递归的话,递归层数最多就是 $\log$ 的层级,我们为了维护区间加和单点查询,我们直接用个树状数组维护即可,在递归的过程中,不断判断模数是否为 $1$,注意快速幂的地方,也要改一下。


代码


#include<iostream>
using namespace std;

#define int long long
const int MAXN = 2 * 1e7 + 5;
int a[MAXN], n, m;
int pri[MAXN], phi[MAXN], tot = 0;
int sum[MAXN];
bool vis[MAXN];

void init(){
	phi[1] = 1;
	for(int i = 2;i < MAXN;i ++){
		if(!vis[i]){
			phi[i] = i - 1;
			pri[++ tot] = i;
		}
		for(int j = 1;j <= tot && i * pri[j] < MAXN;j ++){
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0){
				phi[i * pri[j]] = phi[i] * pri[j];
				break;
			}
			phi[i * pri[j]] = phi[i] * (pri[j] - 1);
		}
	}
//	for(int i = 1;i < 5;i ++){
//		cout << phi[i] << ' ';
//	}
}

int lowbit(int x){
	return x & -x;
}

void add(int x, int k){
	while(x <= n){
		sum[x] += k;
		x += lowbit(x);
	}
}

int ask(int x){
	int res = 0;
	while(x){
		res += sum[x];
		x -= lowbit(x);
	}
	return res;
}

int qpow(int a, int b, int p){
	int res = 1;
	bool flag = 0;
	if(a >= p){
		flag = 1;
		a %= p;
	}
	while(b){
		if(b & 1) res = res * a;
		if(res >= p){
			res %= p;
			flag = 1;
		}
		a = a * a;
		if(a >= p){
			a %= p;
			flag = 1;
		}
		b >>= 1;
	}
	return flag ? res + p : res;
}

int solve(int l, int r, int p){
	int val = a[l] + ask(l);
	if(p == 1) return 1;
	if(l == r){
		if(val >= p) return val % p + p;
		else return val;
	}
	int cnt = solve(l + 1, r, phi[p]);
	return qpow(val, cnt, p);
}

signed main(){
	cin.tie(0) -> ios::sync_with_stdio(0);
	init();
	cin >> n >> m;
	for(int i = 1;i <= n;i ++){
		cin >> a[i];
	}
	for(int i = 1;i <= m;i ++){
		int op, l, r, x;
		cin >> op >> l >> r >> x;
		if(op == 1) add(l, x), add(r + 1, -x);
		else cout << solve(l, r, x) % x << '\n';
	}
	return 0;
}



题目4228  [Ynoi Easy Round 2016] Nephren Ruq Insania      2      评论
2026-02-28 13:31:34    
Gravatar
终焉折枝
积分:1684
提交:221 / 391

更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19648636


大意

区间加,求 $a[l]^{a[l+1]^{a[l+2]^{\dots ^{a[r]}}}} \mod p$ 的值。


思路

对于这个指数塔,非常逆天,我们首先想到的肯定的是与降幂有关的内容,也就只有欧拉定理了,由于 $p$ 并非是质数,我们需要用扩展欧拉定理,那么什么是欧拉定理和扩展欧拉呢?

$\varphi(n)$ 就表示小于等于 $n$ 与 $n$ 互质的数的个数,$\varphi(1) = 1, \varphi(p) = p - 1$,这里的 $p$ 是质数。那么 $\phi(n)$ 应该怎么算呢?我们由素数分解定理,就可以有这样的公式:

$$n \prod_{p | n} (1 - \frac{1}{p})$$

这个过程我们是可以用线性筛预处理这个 $\varphi(n)$ 的。

好的,预处理讲完了,接下来讲欧拉定理,当 $\gcd(a, m) = 1$,那么 $a ^ {\varphi(m)} \equiv 1 \pmod m$,这个在模数为质数的时候就可以快速降幂,但是要是不是质数呢?没事,我们有扩展欧拉定理,其内容如下:

$$a ^ b \equiv \begin{cases} a ^ {b \pmod{\varphi(m)}} & \gcd(a, m) = 1 \\ a ^ b & \gcd(a, m) \neq 1, b < \varphi(m) \\ a ^ {b \pmod{\varphi(m)} + \varphi(m)} & \gcd(a, m) \neq 1, b \ge \varphi(m) \end{cases} \pmod{m}$$

好了,知道了这些,我们回到这个题目,对于这个指数塔,其指数一直取 $\varphi$ 显然最后会变成 $1$,那么要取多少次呢?我们想想对于 $\varphi(2 ^ k)$ 来说,其等于 $2 ^ {k - 1}$,对于质数是 $p - 1$,其如果是质数就减去一变成偶数,然后就会很快变小。

那么我们这个题直接写递归的话,递归层数最多就是 $\log$ 的层级,我们为了维护区间加和单点查询,我们直接用个树状数组维护即可,在递归的过程中,不断判断模数是否为 $1$,注意快速幂的地方,也要改一下。


代码

#include<iostream>
using namespace std;

#define int long long
const int MAXN = 2 * 1e7 + 5;
int a[MAXN], n, m;
int pri[MAXN], phi[MAXN], tot = 0;
int sum[MAXN];
bool vis[MAXN];

void init(){
	phi[1] = 1;
	for(int i = 2;i < MAXN;i ++){
		if(!vis[i]){
			phi[i] = i - 1;
			pri[++ tot] = i;
		}
		for(int j = 1;j <= tot && i * pri[j] < MAXN;j ++){
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0){
				phi[i * pri[j]] = phi[i] * pri[j];
				break;
			}
			phi[i * pri[j]] = phi[i] * (pri[j] - 1);
		}
	}
//	for(int i = 1;i < 5;i ++){
//		cout << phi[i] << ' ';
//	}
}

int lowbit(int x){
	return x & -x;
}

void add(int x, int k){
	while(x <= n){
		sum[x] += k;
		x += lowbit(x);
	}
}

int ask(int x){
	int res = 0;
	while(x){
		res += sum[x];
		x -= lowbit(x);
	}
	return res;
}

int qpow(int a, int b, int p){
	int res = 1;
	bool flag = 0;
	if(a >= p){
		flag = 1;
		a %= p;
	}
	while(b){
		if(b & 1) res = res * a;
		if(res >= p){
			res %= p;
			flag = 1;
		}
		a = a * a;
		if(a >= p){
			a %= p;
			flag = 1;
		}
		b >>= 1;
	}
	return flag ? res + p : res;
}

int solve(int l, int r, int p){
	int val = a[l] + ask(l);
	if(p == 1) return 1;
	if(l == r){
		if(val >= p) return val % p + p;
		else return val;
	}
	int cnt = solve(l + 1, r, phi[p]);
	return qpow(val, cnt, p);
}

signed main(){
	cin.tie(0) -> ios::sync_with_stdio(0);
	init();
	cin >> n >> m;
	for(int i = 1;i <= n;i ++){
		cin >> a[i];
	}
	for(int i = 1;i <= m;i ++){
		int op, l, r, x;
		cin >> op >> l >> r >> x;
		if(op == 1) add(l, x), add(r + 1, -x);
		else cout << solve(l, r, x) % x << '\n';
	}
	return 0;
}


题目4318  数据结构题      2      评论
2026-02-28 13:30:07    
Gravatar
rzzakioi
积分:377
提交:66 / 122

# T5 数据结构题 题解

我们将询问转化为如下问题

定义 $f(l,r)$ $(l \le r)$ 如下:

当 $l=r$ 时,$f(l,r)=a_l$;否则,$f(l,r)=a_l^{f(l+1,r)}$

每次操作 $2$,输出 $f(l,r)\bmod p$

那我们该如何维护呢?

这里我们需要一个前置知识,叫做扩展欧拉定理,即:

$$a^b\equiv \begin{cases}a^{b \bmod \varphi(p)}(b<\varphi(p))\\a^{b \bmod \varphi(p)+\varphi(p)}(b\ge\varphi(p))\end{cases}\pmod p$$

那么

$$f(l,r)\equiv a^{f(l+1,r)\bmod \varphi(p)+[b\ge \varphi(p)]\times \varphi(p)}\pmod p$$

那么,求解 $f(l,r)\bmod p$ 就变成了求解 $f(l+1,r)\bmod \varphi(p)$,并判断 $f(l+1,r)$ 与 $p$ 的关系

然后我们就可以递归求解了

递归边界的话,则有三个

当 $l=r$ 时,$f(l,r)\bmod p\equiv a_l\pmod p$

当 $p=1$ 时,$f(l,r)\bmod p\equiv 0\pmod p$

当 $a_l=1$ 时,$f(l,r)\bmod p\equiv 1\pmod p$

那么这个递归的复杂度是多少呢?

我们分析一下将 $\varphi(p)\to p$ 直到 $p=1$ 的时间复杂度是多少

由 $\varphi(p)=p\prod_{i=1}^{n} \frac{p_i-1}{p_i}$ $(p=\prod_{i=1}^{n}{p_i}^{k_i})$可知

当 $p$ 为偶数时,上述式子必有一项是 $\frac{1}{2}$,所以 $\varphi(p)\le\frac{1}{2}p$

当 $p$ 为奇数时,上述式子的 $p_i-1$ 必是偶数,因此 $\varphi(p)$ 为偶数,回到 $p$ 为偶数的情况

因此最多经过 $O(logn)$ 次递归就可以得出答案

至于判断 $f(l+1,r)$ 与 $p$ 的关系,可以让递归函数返回一个结构体,结构体第一项存 $f(l+1,r)\bmod \varphi(p)$ 的值,第二项存 $f(l+1,r)$ 与 $\varphi(p)$ 的大小关系,前者大于等于后者,这一项就是 $1$,反之则为 $0$

区间和我们用树状数组维护差分即可

时间复杂度:$O(mlogqlogn)$


题目4318  数据结构题 AAAAAAAAAA      评论
2026-02-28 13:04:35    
Gravatar
终焉折枝
积分:1684
提交:221 / 391
真的更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19648133

题目4323  十二重计数法(第一关) AAAAA      4      评论
2026-02-27 20:33:10