BJ集训 5.5 amazed

博客围绕分段一次函数展开,给出定义域和值域均为[0…m]的分段一次函数f,定义fk(x)=fk−1(f(x)),要求解fn(x)=x的x的个数。题解先分析复合过程,用矩阵乘法计算区间方案数,再考虑端点情况并用倍增维护,最后探讨无解情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出一个分段一次函数fff

f的定义域和值域都是[0…m]

形式如下:
给出m,c[0…m]

f就是依次连接(i,c[i])和(i+1,c[i+1])(0<=i<m)的线段所形成的函数

定义:
fk(x)=fk−1(f(x))f^k(x)=f^{k-1}(f(x))fk(x)=fk1(f(x))
f1(x)=f(x)f^1(x)=f(x)f1(x)=f(x)

fn(x)=xf^n(x)=xfn(x)=x的x的个数,若有无穷个,输出-1。

m<=70,1<=n<=1e6

题解:

首先思考清楚复合的过程是什么:
[i,i+1]变成[c[i],c[i+1]]
然后继续分,当然有可能翻转区间。

那么若干次复合后一个区间就会分成若干个小区间(有可能是一个点)

然后我们先不考虑端点的情况,fn(x)=xf^n(x)=xfn(x)=x的条件是什么,那就是[i,i+1]变回了[i,i+1]

那么不妨设f[i][j]f[i][j]f[i][j]表示走了若干步,[i,i+1][i,i+1][i,i+1][j,j+1][j,j+1][j,j+1]区间的方案数。

这个用矩阵乘法搞搞就好了。

再思考端点。

一个段点走了若干次能走回来要算一次。

但是我们可能在前面的区间就算过这个点了。

如果这个点向左走一点点,最后回到了[i−1,i][i-1,i][i1,i],那么就会计算到这个点,右边同理。

这些也可以用倍增维护。

再看无解,即一个区间复合后唯一对应它自己(注意其他点都不能有),这个之前的f[i][j]f[i][j]f[i][j]由于考虑不到点的情况,所以要重新计算。

即严格控制每次的abs(c[i]−c[i−1])=1abs(c[i]-c[i-1])=1abs(c[i]c[i1])=1,这样的去走,再倍增一下就好了。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define mem(a) memset(a, 0, sizeof a)
using namespace std;

const int N = 81;

int T, n, m, c[N];

const int mo = 998244353;

struct jz {
	ll a[N][N];
} a, s;

jz operator *(jz a, jz b) {
	jz c; memset(c.a, 0, sizeof c.a);
	ff(k, 0, m) ff(i, 0, m) if(a.a[i][k])
		ff(j, 0, m) c.a[i][j] += a.a[i][k] * b.a[k][j] % mo;
	ff(i, 0, m) ff(j, 0, m) c.a[i][j] %= mo;
	return c;	
}

int f[2][N], g[N], o;
int u[2][2][N], v[2][N];
int p[N * 2], q[2][N * 2];

int main() {
	scanf("%d", &T);
	fo(ii, 1, T) {
		scanf("%d %d", &n, &m);
		fo(i, 0, m) scanf("%d", &c[i]);
		fo(i, 0, m) fo(j, 0, m) s.a[i][j] = a.a[i][j] = 0;
		ff(i, 0, m) {
			int x = c[i], y = c[i + 1];
			if(x > y) swap(x, y);
			ff(j, x, y) a.a[i][j] ++;
		}
		ff(i, 0, m) s.a[i][i] = 1;
		ll ans = 0;
		{
			int y = n;
			for(; y; y /= 2, a = a * a)
				if(y & 1) s = s * a;
			ff(i, 0, m) ans += s.a[i][i];
		}
		{
			mem(u[!o]);
			fo(i, 0, m) {
				f[o][i] = c[i], g[i] = i;
				
				if(i == 0 || c[i - 1] == c[i]) u[o][0][i] = 2; else
				u[o][0][i] = c[i - 1] > c[i];
				
				if(i == m || c[i + 1] == c[i]) u[o][1][i] = 2; else
				u[o][1][i] = c[i + 1] > c[i];
				
				v[0][i] = 0; v[1][i] = 1;
				
				if(i != 0 && abs(c[i - 1] - c[i]) == 1)
					q[o][i] = c[i] + (c[i - 1] > c[i]) * (m + 1); else
					q[o][i] = 2 * m + 2;
				if(i != m && abs(c[i + 1] - c[i]) == 1)
					q[o][i + m + 1] = c[i] + (c[i] < c[i + 1]) * (m + 1); else
					q[o][i + m + 1] = 2 * m + 2;
				p[i] = i; p[i + m + 1] = i + m + 1;
			}
			p[2 * m + 2] = q[o][2 * m + 2] = 2 * m + 2;
			int y = n;
			for(; y; y /= 2) {
				if(y & 1) {
					fo(i, 0, 1) fo(j, 0, m) {
						if(v[i][j] == 2) continue;
						v[i][j] = u[o][v[i][j]][g[j]];
					}
					fo(i, 0, m) g[i] = f[o][g[i]];
					fo(i, 0, 2 * m) p[i] = q[o][p[i]];
				}
				o = !o;
				fo(i, 0, m) f[o][i] = f[!o][f[!o][i]];
				fo(i, 0, 1) fo(j, 0, m) {
					if(u[!o][i][j] == 2) {
						u[o][i][j] = 2;
						continue;
					}
					u[o][i][j] = u[!o][u[!o][i][j]][f[!o][j]];
				}
				fo(i, 0, 2 * m + 2) q[o][i] = q[!o][q[!o][i]];
			}
		}
		int ye = 0;
		ff(i, 0, m) {
			if(p[i + m + 1] == i + m + 1) {
				ye = 1; break;
			}
		}
		if(ye) { pp("-1\n"); continue;}
		fo(j, 0, m) if(g[j] == j) {
			ans ++;
			ans -= !v[0][j];
			ans -= v[1][j] == 1;
		}
		pp("%lld\n", ans % mo);
	}
}
资源下载链接为: https://pan.quark.cn/s/f989b9092fc5 今天给大家分享一个关于C#自定义字符串替换方法的实例,希望能对大家有所帮助。具体介绍如下: 之前我遇到了一个算法题,题目要求将一个字符串中的某些片段替换为指定的新字符串片段。例如,对于源字符串“abcdeabcdfbcdefg”,需要将其中的“cde”替换为“12345”,最终得到的结果字符串是“ab12345abcdfb12345fg”,即从“abcdeabcdfbcdefg”变为“ab12345abcdfb12345fg”。 经过分析,我发现不能直接使用C#自带的string.Replace方法来实现这个功能。于是,我决定自定义一个方法来完成这个任务。这个方法的参数包括:原始字符串originalString、需要被替换的字符串片段strToBeReplaced以及用于替换的新字符串片段newString。 在实现过程中,我首先遍历原始字符串,查找需要被替换的字符串片段strToBeReplaced出现的位置。找到后,就将其替换为新字符串片段newString。需要注意的是,在替换过程中,要确保替换操作不会影响后续的查找替换,避免遗漏或重复替换的情况发生。 以下是实现代码的大概逻辑: 初始化一个空的字符串result,用于存储最终替换后的结果。 使用IndexOf方法在原始字符串中查找strToBeReplaced的位置。 如果找到了,就将originalString中从开头到strToBeReplaced出现位置之前的部分,以及newString拼接到result中,然后将originalString的查找范围更新为strToBeReplaced之后的部分。 如果没有找到,就直接将剩余的originalString拼接到result中。 重复上述步骤,直到originalStr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值