ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

一场 NOIP 模拟赛

2021-11-18 21:35:24  阅读:171  来源: 互联网

标签:return NOIP int LL leq 一场 bmod 模拟 翻转


日均一千题,题量破百万!

上句为机房顶级魔怔人发言。

考前白嫖模拟赛当然要打,而且质量挺高的(叭

D 太毒瘤了所以跳了,B 卡不动常数了所以不卡了。

\(\mathcal A\)

给定质数 \(p\) 和正整数 \(a,b\),求最小正整数 \(x\) 使得 \((p^a-1)\equiv 1\pmod {p^b-1}\)。

输出 \(x\bmod 998244353\),\(a,b\leq 10^{18},p\leq 10^9\)。

数论签到?认真的吗,虽然确实签到成功了(

都知道 \(a\equiv 1\pmod b\) 的充要条件是 \((a,b)=1\),所以 \(p>2\) 统统无解。

设 \(f(a)=p^a-1\),发现这东西的一些性质:

  1. \(f(a)\bmod f(b)=f(a\bmod b)\)
  2. \(\gcd(f(a),f(b))=f(\gcd(a,b))\)

证了第一个就能推第二个了,根据朴素的辗转相除即可证明。

对于 \(1\),已知 \(f(a)-p^{a-b}f(b)=f(a-b)\),所以取模等价于不断的 \(f(a-kb)\),最终得到 \(f(a\bmod b)\)。

然后就正常的 \(\text{exGcd}\) 没啥大问题,唯一比较麻烦的是有一个 \(y=x-y\times\lfloor f(a)/f(b)\rfloor\)。

即 \(\lfloor a/b\rfloor\) 的求解,可以化为 \((a-a\bmod b)/b\),如果 \(f(b)>0\) 的话直接求逆元就没了,主要是 \(p^b=1\) 的情况。

考虑直接展开原式,时刻记住 \(p^b\equiv 1\pmod {998244353}\),且以下计算都在 \(\bmod 998244353\) 下进行。

\[\frac{p^a-p^{a\bmod b}}{p^b-1} \]

\[=p^{a\bmod b}\times \frac{p^{\lfloor a/b\rfloor}\times b}{p^b-1} \]

\[=p^{a\bmod b}\times \frac{p^{\lfloor a/b\rfloor}}{p^b-1} \]

\[=p^{a\bmod b}+p^{a\bmod b+b}+\cdots +p^{a-b} \]

\[=\lfloor a/b\rfloor\times p^{a\bmod b} \]

于是就可以简单计算了。

但是得到的 \(x\) 有可能是真正的 \(x\),有可能是 \(x-(p^b-1)\)。

而且 \((a,b,x,y)\) 的 \(\text{exGcd}\) 有经典结论 \(|x|\leq b,|y|\leq a\),所以如果是后者,只需要简单加上 \(p^b-1\)。

这个结论可以用归纳法证,它也是把 int 丢进 \(\text{exGcd}\) 不会爆 int 的理论基础。

又不难发现 \(x,y\) 正负交替,所以根据递归层数就可以判断 \(x\) 的正负,于是做完了。

记得特判 \(y|x\) 的无解情况。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const LL P = 998244353;

LL p, a, b;

LL read(){
	LL x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

LL Gcd(LL a, LL b) {
	while(b) {LL c = a; a = b, b = c % b;}
	return a; 
}

LL Pow(LL a, LL b, LL p) {
	LL s = 1;
	for(; b; b >>= 1) {
		if(b & 1) s = s * a % p;
		a = a * a % p;
	}
	return s;
}

LL F(LL a) {return (Pow(2, a, P) + P - 1) % P;} 

LL Get(LL a, LL b) {
	LL y = F(b);
	if(! y) return (a / b) * Pow(2, a % b, P) % P;
	return (F(a) - F(a % b) + P) % P * Pow(y, P - 2, P) % P;
}

bool exGcd(LL a, LL b, LL &x, LL &y) {
	if(! b) {x = 1, y = 0; return false;}
	bool o = exGcd(b, a % b, x, y);
	LL z = x; x = y, y = ((z - y * Get(a, b) % P) % P + P) % P;
	return ! o;
}

int main() {
	p = read(), a = read(), b = read();
	if(p > 2 || !(a % b) || Gcd(a, b) != 1) {puts("-1"); return 0;}
	LL x, y;
	bool o = exGcd(a, b, x, y);
	if(o) x = (x + F(b)) % P;
	printf("%lld\n", (x % P + P) % P);
	return 0;
}

\(\mathcal B\)

给定长度为 \(n\) 的数列 \(a\) 和目标数列 \(b\),其中的数字都在 \([1,m]\) 中,在 \(\leq 60000\) 个操作内构造方案将 \(a\) 变成 \(b\)。

一次操作形如 \((p,a_1,a_2,\cdots,a_m)\),表示将数列内从 \(p\) 开始的一个 \(m\) 的排列重新排成另一个 \(m\) 的排列 \(a\)。

\(n\leq 200,m\leq 10\)。

有解的充要条件是:一开始两个数列就相等,或对应数字个数一样 且 其中原本都至少有一个排列。

将需要改变的位置根据在 \(b\) 中的位置标记,利用排列不难交换相邻项,根据冒泡排序就可以做到 \(O(n^2)\)。

看着简单写起来要命,而且重度卡常,最后两个点都是 \(67000\),大常数选手老泪纵横(

#include<bits/stdc++.h>
using namespace std;

const int N = 210, M = 6e6 + 10;
int T, n, m, tot, p, q;
int a[N], b[N], s[N];
bool vis[N];
vector<int> sa[N], sb[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}
struct Ans {int p, a[11];} ans[M];

void Clear() {
	tot = p = q = 0;
	for(int i = 1; i <= n; i ++) {
		a[i] = b[i] = s[i] = vis[i] = 0;
		sa[i].clear();
		sb[i].clear();
	}
}

void Back(int o) {
	if(! o) return;
	while(o --) {
		int v = a[p + m];
		ans[++ tot].p = p;
		ans[tot].a[1] = v;
		int now = 1;
		for(int i = 2; i <= m; i ++) {
			if(now == v) now ++;
			ans[tot].a[i] = now; now ++;
		}
		for(int i = 1; i <= m; i ++)
			a[p + i - 1] = ans[tot].a[i];
		s[p] = s[p + m];
		s[p + m] = 0;
		p ++;
	}
}

void Front(int o) {
	if(! o) return;
	while(o --) {
		int v = a[p - 1];
		ans[++ tot].p = p;
		ans[tot].a[m] = v;
		int now = 1;
		for(int i = 1; i <  m; i ++) {
			if(now == v) now ++;
			ans[tot].a[i] = now; now ++;
		}
		for(int i = 1; i <= m; i ++)
			a[p + i - 1] = ans[tot].a[i];
		s[p + m - 1] = s[p - 1];
		s[p - 1] = 0;
		p --;
	}
}

bool Sorted() {
	bool flag = true;
	for(int i = 1; i < n - m; i ++) if(s[i] > s[i + 1]) {flag = false; break;}
	return flag;
}

void Swap(int x, int y) {
	if(a[x] == a[y]) {swap(s[x], s[y]); return;}
	Front(p - y - 1);
	int v1 = a[x];
	int v2 = a[y];
	int now = 1;
	ans[++ tot].p = p;
	for(int i = 1; i <= m - 2; i ++) {
		while(now == v1 || now == v2) now ++;
		ans[tot].a[i] = now; now ++;
	}
	ans[tot].a[m - 1] = v1;
	ans[tot].a[m] = v2;
	for(int i = 1; i <= m; i ++) a[p + i - 1] = ans[tot].a[i];
	
	now = 1;
	ans[++ tot].p = p - 2;
	for(int i = 3; i <= m; i ++) {
		while(now == v1 || now == v2) now ++;
		ans[tot].a[i] = now; now ++;
	}
	ans[tot].a[1] = v2;
	ans[tot].a[2] = v1;
	for(int i = 1; i <= m; i ++) a[p - 2 + i - 1] = ans[tot].a[i];
	swap(s[x], s[y]);
}

void Swap_(int x, int y) {
	if(a[x] == a[y]) {swap(s[x], s[y]); return;}
	Back(x - m - p);
	int v1 = a[x];
	int v2 = a[y];
	int now = 1;
	ans[++ tot].p = p;
	for(int i = 3; i <= m; i ++) {
		while(now == v1 || now == v2) now ++;
		ans[tot].a[i] = now; now ++;
	}
	ans[tot].a[1] = v1;
	ans[tot].a[2] = v2;
	for(int i = 1; i <= m; i ++) a[p + i - 1] = ans[tot].a[i];
	
	now = 1;
	ans[++ tot].p = p + 2;
	for(int i = 1; i <= m - 2; i ++) {
		while(now == v1 || now == v2) now ++;
		ans[tot].a[i] = now; now ++;
	}
	ans[tot].a[m - 1] = v2;
	ans[tot].a[m] = v1;
	for(int i = 1; i <= m; i ++) a[p + 2 + i - 1] = ans[tot].a[i];
	swap(s[x], s[y]);
}

void Sort() {
	while(! Sorted()) {
		Back(n - m + 1 - p);
		for(int i = n - m - 1; i >= 1; i --)
			if(s[i] > s[i + 1]) Swap(i, i + 1);
		if(Sorted()) break;
		Front(p - 1);
		for(int i = m + 1; i < n; i ++)
			if(s[i] > s[i + 1]) Swap_(i, i + 1);
	}
}

int Get(int a[]) {
	int num[15];
	memset(num, 0, sizeof(num));
	for(int i = 1; i < m; i ++) num[a[i]] ++;
	for(int i = m; i <= n; i ++) {
		num[a[i]] ++;
		num[a[i - m]] --;
		bool flag = true;
		for(int j = 1; j <= m; j ++) if(! num[j]) {flag = false; break;}
		if(flag) return i - m + 1;
	}
	return 0;
}

void Work() {
	n = read(), m = read();
	Clear();
	for(int i = 1; i <= n; i ++) a[i] = read(), sa[a[i]].push_back(i);
	for(int i = 1; i <= n; i ++) b[i] = read(), sb[b[i]].push_back(i);
	for(int i = 1; i <= m; i ++)
		if(sa[i].size() != sb[i].size()) {puts("NO"); return;}
	bool flag = true;
	for(int i = 1; i <= n; i ++) if(a[i] != b[i]) {flag = false; break;}
	if(flag) {puts("YES"); puts("0"); return;}

	p = Get(a), q = Get(b);
	if(! p || ! q) {puts("NO"); return;}
	
	for(int i = 1; i <= m; i ++)
		random_shuffle(sb[i].begin(), sb[i].end());
	
	Back((n - m + 1) - p);
	for(int i = 1; i <= n - m; i ++) {
		int v = a[i];
		for(int j = 0; j < (int) sb[v].size(); j ++) {
			int p = sb[v][j];
			if(vis[p] || (p >= q && p <= q + m - 1)) continue;
			s[i] = p, vis[p] = true; break;
		}
	}
	Sort();

	if(p < q) Back(q - p);
	if(p > q) Front(p - q);
	
	ans[++ tot].p = p;
	for(int i = 1; i <= m; i ++) ans[tot].a[i] = b[q + i - 1];
	
	puts("YES");
	int sum = 0;
	for(int i = 1; i <= tot; i ++) sum += (i == tot || ans[i].p != ans[i + 1].p);
	printf("%d\n", sum);
	for(int i = 1; i <= tot; i ++) if(i == tot || ans[i].p != ans[i + 1].p) {
		printf("%d ", ans[i].p);
		for(int j = 1; j <= m; j ++) printf("%d ", ans[i].a[j]);
		puts("");
	} 
}

int main() {
	int T = read();
	while(T --) Work();
	return 0;
}

\(\mathcal C\)

给定 \(n\) 个节点 \(m\) 条边的无向图,每条边有初始权值 \(0/1\)。

每次可以翻转一条边,同时翻转所有和它有共同端点的边(自己只翻转一次),求构造翻转方案使得所有边都为 \(0\)。

数据保证有解,\(n\leq 1000,m\leq n\times (n-1)/2\)。

直接用边异或消元可以得到 50pts 的好成绩,必须考虑把消元扔到点上。

考虑一个点为 \(1\) 表示将与它相连的边都翻转,那么一次边的翻转可以视作 \(u,v,(u,v)\) 两点一边的翻转。

全部为 \(0\) 的充要条件是 每个点 和 与它相连的所有边 的异或和为 \(0\)。

考虑将每个点的这个异或和记为 \(E\),能影响到它的点记为 \(V\)。

对于边 \((u,v)\),显然 \(E(u)\to v,E(v)\to u\),然后对于所有度数为偶数的点 \(E(u)\to u\)。

因为将这个点翻转后,点权 与 所有相连的边权 的异或和必变。(奇度点则必不变)

然后所有 \(V\),初始只有 \(V(u)\to u\)。

然后两个同步消元,得到影响每个点的其他点。

然后根据初始连边情况判断一个点需不需要翻转,要的话就将所有能影响到它的点翻转。

最后把要翻转的点翻转一边就知道了每条边要不要翻转了。

利用 bitset 优化是基本操作了,时间复杂度 \(O(n^3/w)\)。

#include<bits/stdc++.h>
using namespace std;

const int N = 1010, M = N * N;
int n, m, a[M];
int d[N], d1[N], d2[N];
vector<int> G[N];
bitset<N> E[N], V[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= m; i ++) {
		int u = read(), v = read(); a[i] = read();
		E[u].flip(v);
		E[v].flip(u);
		G[u].push_back(i);
		G[v].push_back(i);
		if(a[i])
			d1[u] ^= 1, d1[v] ^= 1;
		d[u] ^= 1, d[v] ^= 1;
	}
	for(int i = 1; i <= n; i ++) {
		if(! d[i]) E[i].flip(i);
		V[i].flip(i);
	}
	for(int i = 1; i <= n; i ++) {
		int k = 0;
		for(int j = i; j <= n; j ++) if(E[j][i]) {k = j; break;}
		if(! k) continue;
		swap(E[k], E[i]);
		swap(V[k], V[i]);
		for(int j = 1; j <= n; j ++) if(j != i && E[j][i]) E[j] ^= E[i], V[j] ^= V[i];
	}
	for(int i = 1; i <= n; i ++) if(d1[i])
		for(int j = 1; j <= n; j ++) if(V[i][j]) d2[j] ^= 1;
	for(int i = 1; i <= n; i ++) if(d2[i])
		for(int u : G[i]) a[u] ^= 1;
	int tot = 0;
	for(int i = 1; i <= m; i ++) if(a[i]) tot ++;
	printf("%d\n", tot);
	for(int i = 1; i <= m; i ++) if(a[i]) printf("%d ", i); puts("");
	return 0;
}

$$\mathcal{NOIP2021\ RP++}$$

标签:return,NOIP,int,LL,leq,一场,bmod,模拟,翻转
来源: https://www.cnblogs.com/lpf-666/p/15574523.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有