The 2022 ICPC Asia Regionals Online Contest (II) 2022ICPC第二场网络赛 ABEFGJKL题解
文章目录
- A Yet Another Remainder【费马小定理】
- B Non-decreasing Array【线性DP】
- E An Interesting Sequence【签到】
- F Infinity Tree 【签到】
- G Good Permutation【排列组合,树形结构】
- J A Game about Increasing Sequences【博弈】
- K Black and White Painting【模拟】
- L Quadruple【简单容斥】
A Yet Another Remainder【费马小定理】
首先明确x是无法求解的,当x大于1e6,一共有1e4个方程组,不可能求解1e6个未知数。
因为题目将x的各个十进制位拆开,并且按照不同的步长分组,因此可能是通过计算组内的值,再将各组的值拼接起来得到xmodpx\ mod\ px mod p的结果。
ppp都是小于100的数,modpmod\ pmod p的结果必然都是100以内的值,因此推测modpmod\ pmod p的结果可能会产生某种规律,比如形成循环节出现。
到这里没有什么新的思路,因此可以翻翻书找找跟质数和模数有关的定理来辅助解题…最常见最相关的就是费马小定理了!
费马小定理: 如果p是一个质数,而整数a不是p的倍数,则有a(p−1)≡1a^{(p-1)}≡1a(p−1)≡1(mod p)。
费马小定理中的ppp就是我们题目中的ppp,那么aaa可以是什么呢?题目将xxx按照10进制进行拆分,因此aaa可以是10i10^i10i,可惜发现p=5p=5p=5时10i10^i10i是5的倍数,但是!题目非常细心地将p=5p=5p=5的情况给剔除掉了!因此可以确定这个方向应该是正确的!
接下来就是利用费马小定理给xmodpx\ mod\ px mod p进行分组求和了。因为10p−1≡1(modp)10^{p-1}≡1(mod\ p)10p−1≡1(mod p),因此有102(p−1)≡(modp),103(p−1)≡(modp)...10s(p−1)≡(modp)10^{2(p-1)}≡(mod\ p),\ 10^{3(p-1)}≡(mod\ p)...\ 10^{s(p-1)}≡(mod\ p)102(p−1)≡(mod p), 103(p−1)≡(mod p)... 10s(p−1)≡(mod p),其中s表示最后一项。因此可以将10k(p−1)≡1(modp)10^{k(p-1)}≡1(mod\ p)10k(p−1)≡1(mod p)当成一组同时求解。而在第p−1p-1p−1行,给定的恰好是以p−1p-1p−1为步长的十进制位的和。
因为x=10n−1∗a1+10n−2∗a2+...+10n−i∗ai+100∗anx=10^{n-1}*a^1+10^{n-2}*a^2+...+ 10^{n-i}*a^i+10^0*a^nx=10n−1∗a1+10n−2∗a2+...+10n−i∗ai+100∗an,可知找到10s(p−1)10^{s(p-1)}10s(p−1)对应的是a数组的第n−s(p−1)n-s(p-1)n−s(p−1)项,即bp−1,n−s(p−1)b_{p-1,n-s(p-1)}bp−1,n−s(p−1)。
将10k(p−1)+1,k=0,1,2,...,s10^{k(p-1)+1},\ k=0,1,2,...,s10k(p−1)+1, k=0,1,2,...,s的项作为一组,将10k(p−1)+2,k=0,1,2,...,s10^{k(p-1)+2},\ k=0,1,2,...,s10k(p−1)+2, k=0,1,2,...,s作为一组。
即将x拆解为(a1+a1+(p−1)+a1+2(p−1)+...+a1+s(p−1))∗10(n−1)%(p−1))+(a2+a2+(p−1)+a2+2(p−1)+...+a2+s(p−1))∗10(n−2)%(p−1))+...+(ap−1+ap−1+(p−1)+ap−1+2(p−1)+...+ap−1+s(p−1))∗10(n−p+1)%(p−1))(a_1+a_{1+ (p-1)}+a_{1+2(p-1)}+...+a_{1+s(p-1)})*10^{(n-1)\%(p-1)})+\\(a_2+a_{2+ (p-1)}+a_{2+2(p-1)}+...+a_{2+s(p-1)})*10^{(n-2)\%(p-1)})+\\...+\\(a_{p-1}+a_{p-1+ (p-1)}+a_{p-1+2(p-1)}+...+a_{p-1+s(p-1)})*10^{(n-p+1)\%(p-1)})(a1+a1+(p−1)+a1+2(p−1)+...+a1+s(p−1))∗10(n−1)%(p−1))+(a2+a2+(p−1)+a2+2(p−1)+...+a2+s(p−1))∗10(n−2)%(p−1))+...+(ap−1+ap−1+(p−1)+ap−1+2(p−1)+...+ap−1+s(p−1))∗10(n−p+1)%(p−1))
用b表示为bp−1,1∗10(n−1)%(p−1)+bp−1,2∗10(n−2)%(p−1)+...+bp−1,p−1∗10(n−p+1)%(p−1)b_{p-1,1}*10^{(n-1)\%(p-1)}+b_{p-1,2}*10^{(n-2)\%(p-1)}+...+b_{p-1,p-1}*10^{(n-p+1)\%(p-1)}bp−1,1∗10(n−1)%(p−1)+bp−1,2∗10(n−2)%(p−1)+...+bp−1,p−1∗10(n−p+1)%(p−1)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 110;
int b[N][N];
int main()
{int t; scanf("%d", &t);while(t --){int n; scanf("%d", &n);for(int i = 1; i <= min(n, 100); i ++)for(int j = 1; j <= i;j ++)scanf("%d", &b[i][j]);if(n <= 100){int q; scanf("%d", &q);while(q --){int p; scanf("%d", &p);int ans = 0, pow = 1;for(int i = n; i; i --){ans = (ans + b[n][i] * pow) % p;pow = 10 * pow % p;}printf("%d\n", ans);}continue;}int q; scanf("%d", &q);while(q --){int p; scanf("%d", &p);int ans = 0;int pow10[100] = {0};pow10[0] = 1;for(int i = 1; i <= p; i ++) pow10[i] = pow10[i - 1] * 10 % p;for(int i = 1; i < p; i ++)ans = (ans + pow10[(n - i) % (p - 1)] * b[p - 1][i] % p) % p;printf("%d\n", ans);}}return 0;
}
B Non-decreasing Array【线性DP】
赛时没过题,一直往区间dp想,复杂度是n4n^4n4的,很难优化。但是dp最简单的就是从头到尾推的线性dp啊…下次思路卡住就看看书吧!dp入门!
先简单地证明一下,将一个数字删除总是会得到更好的结果
假设现在的数组是a,b,c(c>=b>=a)a,b,c(c>=b>=a)a,b,c(c>=b>=a),三个数对答案的贡献是(c−b)2+(b−a)2(c-b)^2+(b-a)^2(c−b)2+(b−a)2,而将中间的数删除之后,对答案的贡献变成(c−a)2=((c−b)+(b−a))2=(c−b)2+(b−a)2+2(c−b)(b−a)(c-a)^2=((c-b)+(b-a))^2=(c-b)^2+(b-a)^2+2(c-b)(b-a)(c−a)2=((c−b)+(b−a))2=(c−b)2+(b−a)2+2(c−b)(b−a),因为c>=b,b>=ac>=b,b>=ac>=b,b>=a,因此每删除一个中间数,答案就相比原来增加2*中间数与前数的差*后数与前数的差。删除一个数,答案增加如上所述,而改变一个数,其实最佳方式就是直接将其变成它的某个相邻数,因为相邻差变为0,对答案不再有贡献,其实和删除的操作是一样的。因此第kkk次操作,就是选两个数删除掉。
先记录起始的答案,接下来只有相邻差有用,而原来的数已经没有用了,因此直接记录它们的相邻差数组difdifdif即可。接下来的问题就像石子合并:一开始有n−1n-1n−1堆石子,每堆石子的数量为dif[i]dif[i]dif[i],每次选择相邻的两堆石子i,ji,ji,j,答案增加2∗dif[i]∗dif[j]2*dif[i]*dif[j]2∗dif[i]∗dif[j],然后将两堆石子合并起来。
f[i][j]f[i][j]f[i][j]表示从第一堆石子到第iii堆石子,已经合并了jjj堆石子获得的最大答案。
接下来思考如何更新f[i][j]f[i][j]f[i][j]。只需要考虑最后第iii堆和前面多少堆连续合并在一起即可。
- 第iii堆不合并,直接从f[i−1][j]f[i-1][j]f[i−1][j]更新过来。
- 第iii堆只和第i−1i-1i−1合并,答案为f[i−2][j−1]f[i-2][j-1]f[i−2][j−1]再加上第iii堆和第i−1i-1i−1堆合并的新答案。
- 第iii堆和第i−1,i−2i-1,i-2i−1,i−2堆合并,一共合并2次,答案为f[i−3][j−2]f[i-3][j-2]f[i−3][j−2]再加上后面三堆合并的新答案。
依次类推即可。
note: 可预处理出数组g[i][j]g[i][j]g[i][j]表示从iii到jjj连续段石子合并的答案。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 110;
LL g[N][N], f[N][N], dif[N], pre[N];
int a[N];
int main()
{int n; cin >> n;for(int i = 1; i <= n; i ++) cin >> a[i];LL ans = 0;for(int i = 1; i < n; i ++) dif[i] = a[i + 1] - a[i], ans += dif[i] * dif[i], pre[i] = pre[i - 1] + dif[i];//预处理出从i到j连续段的答案贡献n --;for(int i = 1; i < n; i ++)for(int j = i + 1; j <= n; j ++)g[i][j] = g[i][j - 1] + 2 * dif[j] * (pre[j - 1] - pre[i - 1]);for(int i = 1; i <= n; i ++)for(int j = 0; j < i; j ++)for(int k = 1; k <= i; k ++)if(j - i + k >= 0)f[i][j] = max(f[i][j], f[k - 1][j - i + k] + g[k][i]);for(int i = 1; i <= n + 1; i ++){printf("%lld\n", ans + f[n][min(i * 2, n - 1)]);}return 0;
}
E An Interesting Sequence【签到】
找到第一个和k互质的数,然后一直放2323…或者3232…即可。
F Infinity Tree 【签到】
观察发现,第n秒树的大小为(k+1)n(k+1)^n(k+1)n,不难推出,节点x的父亲节点为(x−t−1)/k+1(x - t - 1) / k + 1(x−t−1)/k+1,其中ttt为第一个小于x的(k+1)n(k+1)^n(k+1)n,每次暴力找到第一个小于x和小于y的节点,一直往上跳即可。
G Good Permutation【排列组合,树形结构】
考虑最简单的情况,只有[1,n][1,n][1,n]一个区间,答案就是n!n!n!。如果[1,n][1,n][1,n]中包含333个区间限制,恰好覆盖[1,n][1,n][1,n]整个区间,长度分别为len1,len2,len3len1,len2,len3len1,len2,len3,答案是3!∗len1!∗len2!∗len3!3!*len1!*len2!*len3!3!∗len1!∗len2!∗len3!。3!3!3!表示将3个限制区间在[1,n][1,n][1,n]之间进行全排列,len1!len1!len1!表示被分到第1个区间的连续数字的排列方案。但是如果区间1还有2个小区间,长度分别为len11,len12len11,len12len11,len12,这时区间1的方案数就变成2!∗len11!∗len122!*len11!*len122!∗len11!∗len12,全区间的方案数也会从3!∗len1!∗len2!∗len3!3!*len1!*len2!*len3!3!∗len1!∗len2!∗len3!变成3!∗2!∗len11!∗len12∗len2!∗len3!3!*2!*len11!*len12*len2!*len3!3!∗2!∗len11!∗len12∗len2!∗len3!。因此应该先将小区间更新,更新完小区间再用小区间更新大区间。
因为每个区间只有包含和并列关系,因此构成了树形结构,可以给每个区间一个编号。然后排序乱搞一通获得一棵树结构。先利用set将区间去重,然后按照左端点从小到大,右端点从大到小排序。这样排序可保证后面的区间必然不是前面的区间的父亲,因此每次记录上一次排序结束的区间节点preprepre,当前区间的节点为curcurcur,则curcurcur的父亲就是curcurcur和preprepre的最近公共祖先,因为preprepre和curcurcur区间中间已经没有隔着其他区间了。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
struct Seg
{int l, r, len, fa;bool operator<(const Seg &x)const{if(l == x.l) return x.r < r;return l < x.l;}bool operator=(const Seg &x)const{return l == x.l && r == x.r;}
}seg[N];
set<Seg> tmp_seg;
int n, m;
int fac[N];
vector<int> son[N];
int dfs(int u)
{int ans = 1, len = 0;for(auto v : son[u]){ans = 1ll * ans * dfs(v) % mod;len += seg[v].len;}int left = seg[u].len - len, sz = left + son[u].size();ans = 1ll * fac[sz] * ans % mod;return ans;
}
int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= m; i ++){int l, r; scanf("%d%d", &l, &r);if(l == 1 && r == n) continue;tmp_seg.insert({l, r, r - l + 1, 0});}int cnt = 1;seg[cnt].l = 1, seg[cnt].r = n, seg[cnt].len = n;for(auto it : tmp_seg){int pre = cnt; cnt ++;seg[cnt].l = it.l, seg[cnt].r = it.r, seg[cnt].len = it.len;while(seg[pre].r < seg[cnt].r) pre = seg[pre].fa;seg[cnt].fa = pre;son[pre].push_back(cnt);}fac[0] = 1;for(int i = 1; i <= max(m, n); i ++) fac[i] = 1ll * fac[i - 1] * i % mod;printf("%d\n", dfs(1));return 0;
}
J A Game about Increasing Sequences【博弈】
可以发现,只有数列从左向右最长的一段递增序列和从右向左最长的一段递增序列有用。
考虑这两段序列的起始元素,如果两个元素大小相同,且两段序列中存在一段奇数长度的序列则先手必胜。
否则:
如果大的那个元素对应的序列长度为奇数,那么先手必胜;
不然先手必然选小的那个元素,然后轮到后手选;
我们直接模拟一遍即可得出结果。
K Black and White Painting【模拟】
因为整个网格的大小只有200*200,因此考虑标记每个网格的有贡献的边和圆弧来统计答案。用左下角的点来表示网格。对于每个网格,直线边有上下左右四条边,网格内可能存在四种圆弧。因此只需要考虑边和圆弧的所有组合情况即可。
某个网格里圆弧组合为02 或者13,则四条边和所有圆弧都会被覆盖失效。
若整个网格都某个正方形覆盖,那么不管网格里的所有圆弧都会失效,枚举网格四条边看是否存在一条正方形边,且不会被其他的图形覆盖。比如判断网格左边是否有贡献,要检查:网格左侧有正方形边,当前网格左侧的网格没有整个网格被覆盖且没有23形状的圆弧。
存在两段圆弧(除了02组合和13组合的其他圆弧组合情况),都会有1/3的圆弧贡献
存在一段圆弧,都会有1/2的圆弧贡献。
#include<bits/stdc++.h>
using namespace std;
const int N = 210, mod = 998244353;
bool a[N][N], b[N][N][4], c[N][N][2];
int qpow(int a, int b)
{int ans = 1;while(b){if(b & 1) ans = 1ll * ans * a % mod;a = 1ll * a * a % mod;b >>= 1;}return ans;
}
bool ckl(int i, int j){return (!a[i][j - 1] && !b[i][j - 1][2] && !b[i][j - 1][3]) && c[i][j][0];}
bool ckr(int i, int j){return (!a[i][j + 1] && !b[i][j + 1][0] && !b[i][j + 1][1]) && c[i][j + 1][0];}
bool cku(int i, int j){return (!a[i - 1][j] && !b[i - 1][j][0] && !b[i - 1][j][3]) && c[i - 1][j][1];}
bool ckd(int i, int j){return (!a[i + 1][j] && !b[i + 1][j][1] && !b[i + 1][j][2]) && c[i][j][1];}
int main()
{int n; cin >> n;for(int i = 1; i <= n; i ++){int op, x, y; cin >> op >> x >> y;x += 102, y += 102;if(op == 1)a[x][y] = a[x + 1][y] = a[x + 1][y - 1] = a[x][y - 1] = true,c[x][y - 1][0] = c[x][y + 1][0] = c[x + 1][y - 1][0] = c[x + 1][y + 1][0] = true,c[x - 1][y - 1][1] = c[x - 1][y][1] = c[x + 1][y - 1][1] = c[x + 1][y][1] = true;else b[x][y][0] = b[x + 1][y][1] = b[x + 1][y - 1][2] = b[x][y - 1][3] = true;}int r1 = 0, r2 = 0;for(int i = 1; i < N - 1; i ++){for(int j = 1; j < N - 1; j ++){if((b[i][j][0] && b[i][j][2]) || (b[i][j][1] && b[i][j][3])) continue;else if(a[i][j]) r1 += ckl(i, j) + ckr(i, j) + cku(i, j) + ckd(i, j);else if((b[i][j][0] || b[i][j][2]) && (b[i][j][1] || b[i][j][3])) r2 += 2;else if(b[i][j][0] || b[i][j][1] || b[i][j][2] || b[i][j][3]) r2 += 3;}}printf("%d %d\n", r1, 1ll * r2 * qpow(6, mod - 2) % mod);return 0;
}
L Quadruple【简单容斥】
首先不难想到处理出ICPC的前缀和来对付区间询问,但是这样会算多一些,因为区间外的I与区间内的CPC组成的ICPC、区间外的IC和区间内的PC组成的ICPC和区间外的ICP和区间内的C组成的ICPC都会被算进去。于是我们减掉这些即可。CPC的方案数和PC的方案数计算方法也是类似的。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 2e6 + 10, mod = 998244353;
char s[N];
int n, q, u[N], v[N];
struct Int
{int a;Int(){}Int(int _a){a = _a;}Int operator + (const Int &b)const {return Int{(a + b.a) % mod};}Int operator - (const Int &b)const {return Int{(a - b.a + mod) % mod};}Int operator * (const Int &b)const {return Int{1ll * a * b.a % mod};}
}I[N], C[N], P[N], IC[N], CP[N], PC[N], ICP[N], CPC[N], ICPC[N];
int get(int l, int r)
{Int sicpc = ICPC[r] - ICPC[l - 1];Int spc = PC[r] - PC[l - 1] - P[l - 1] * (C[r] - C[l - 1]);Int scpc = CPC[r] - CPC[l - 1] - CP[l - 1] * (C[r] - C[l - 1]) - C[l - 1] * spc;sicpc = sicpc - scpc * I[l - 1] - IC[l - 1] * spc - ICP[l - 1] * (C[r] - C[l - 1]);return sicpc.a;
}
int main()
{scanf("%d%d", &n, &q);scanf("%s", s + 1);int x, a, b, p, ans = 0;scanf("%d%d%d%d", &x, &a, &b, &p);for(int i = 1; i <= n; i ++){ICPC[i] = ICPC[i - 1] + ICP[i - 1] * (s[i] == 'C');ICP[i] = ICP[i - 1] + IC[i - 1] * (s[i] == 'P'), CPC[i] = CPC[i - 1] + CP[i - 1] * (s[i] == 'C');IC[i] = IC[i - 1] + I[i - 1] * (s[i] == 'C'), CP[i] = CP[i - 1] + C[i - 1] * (s[i] == 'P'), PC[i] = PC[i - 1] + P[i - 1] * (s[i] == 'C');I[i] = I[i - 1] + (s[i] == 'I'), C[i] = C[i - 1] + (s[i] == 'C'), P[i] = P[i - 1] + (s[i] == 'P');}for(int i = 1; i <= n; i ++) u[i] = x = (1ll * a * x + b) % p, u[i] = u[i] % n + 1;for(int i = 1; i <= n; i ++) v[i] = x = (1ll * a * x + b) % p, v[i] = v[i] % n + 1;for(int i = 1; i <= n; i ++) ans = (ans + get(min(u[i], v[i]), max(u[i], v[i]))) % mod;printf("%d\n", ans);return 0;
}
The 2022 ICPC Asia Regionals Online Contest (II) 2022ICPC第二场网络赛 ABEFGJKL题解相关推荐
- The 2022 ICPC Asia Regionals Online Contest (II) A、B、E、F、G、J、L
文章目录 A-Yet Another Remainder 题目 题解 B-Non-decreasing Array 题目 题解 E-An Interesting Sequence 题目 题解 F-In ...
- The 2022 ICPC Asia Regionals Online Contest (II) J
J:A Game about Increasing Sequences 不是特别会博弈,只能说一下大概意思 Alice and Bob like playing games. The game is ...
- The 2022 ICPC Asia Regionals Online Contest (II) B
B : Non-decreasing Array You are given a non-decreasing array of integers a1,a2,-,an. In one oper ...
- The 2022 ICPC Asia Regionals Online Contest (I)
D题 The 2022 ICPC Asia Regionals Online Contest (I) 03:14:13 H Step Debugging 作者 pku 单位 北京大学 Rikka is ...
- 2021 ICPC Asia Regionals Online Contest (II) Problem G. Limit
The 2021 ICPC Asia Regionals Online Contest (II) Problem G. Limit 在欧教的指导下,复习了下高数知识,写下了这题的题解- 做这道题之前, ...
- 【ICPC 2021网络赛2】The 2021 ICPC Asia Regionals Online Contest (II)签到题5题
M. Addition 题意: 给出n,接下来三行,每行n位二进制数,分别表示符号sgn{-1,1}和a{0,1}, b{0,1}. 令c=a+b(a和sgn每位相乘得到数a),最后将c拆成每一位输出 ...
- 2021ICPC网络赛第二场The 2021 ICPC Asia Regionals Online Contest (II) 【L Euler Function】
分析: 根据欧拉函数的那个性质 if(p是质数){if(i % p == 0) f[i * p] = f[i] * p;else f[i * p] = f[i] * (p - 1);} 每次区间乘的那 ...
- The 2022 ICPC Asia Regionals Online Contest - A 01 Sequence
01 Sequence 题目 Given a binary cyclic sequence S of length n, whose elements are either 0 or 1, you c ...
- The 2021 ICPC Asia Regionals Online Contest (II)
比赛链接 A. Sort 暴力 k=1k=1k=1 检查数组是否有序: k=2k=2k=2 相当于再环上找个起点使得数组有序,直接判断: k≥3k\ge 3k≥3 考虑插入排序,每次暴力找到第 iii ...
最新文章
- jenkins 修改工作目录
- Java多线程编程核心技术笔记
- Scala模式匹配:对元组进行匹配
- gdb调试fork多进程
- Flask入门之上传文件到服务器
- 为什么可以通过类名调用静态方法?
- 2020年中国便利店发展报告
- “烟花”来势汹汹!用数据可视化告诉你:台风最爱在哪登陆?
- php post请求后端拿不到值_PHP解决Vue发起POST请求,接收不到数据
- vue 上次登录时间_vue实现登录之后长时间未操作,退出登录
- Ubuntu查看crontab运行日志
- dialog的二次封装
- mysql卸载不干净时,如何干净利索的卸载mysql数据库(完整版)
- Kfc点餐系统 小程序
- 小岚rabbit_radish(兔仔-萝卜)
- 根据IP地址查询所在地
- 无效的月份oracle,Oracle插入失败:无效的月份
- win10 租约到期不可连网
- oracle脏读如何解决,关于脏读分析
- Errors occurred during the build. Errors running builder 'JavaScript Validator'