ARC166
ARC166
前言
到了 e 难度莫名陡增.......
[ARC166A] Replace C or Swap AB
一个比较简单的贪心
进行交换操作肯定会减少 C 的个数。并且唯一可以变序位置的操作是操作三,所以最后结果一定是 B 尽可能在前。等价于把某个 A 向后移一个位置。贪心地把靠前的 C 都替换成 A。判断当前 X 中是否每个对应的 A 所在位置都不大于它在 Y 中的目标位置即可。
signed main()
{
int T;
cin >> T;
while (T--)
{
int n; string x, y;
cin >> n >> x >> y;
deque<int> q;
bool flag = 0;
for (rint i = 0; i < n; i++)
{
if (y[i] == 'C')
{
if (x[i] != 'C')
{
puts("No");
flag = 1;
break;
}
else q.push_back((int)i);
}
}
if (flag) continue;
int cnt = 0;
q.push_back(n);
while (!q.empty())
{
int a = q.front();
q.pop_front();
int a1 = 0, c1 = 0;
for (rint i = cnt; i < a; i++)
{
if (x[i] == 'A') a1++;
if (x[i] == 'C') c1++;
if (y[i] == 'A')
{
if (!a1 && !c1)
{
puts("No");
flag = 1;
break;
}
if (!a1) c1--;
else a1--;
}
}
if (flag) break;
if (a1 > 0)
{
puts("No");
flag = 1;
break;
}
cnt = a + 1;
}
if (flag) continue;
puts("Yes");
}
return 0;
}
[ARC166B] Make Multiples
传送门link
直接 dp,其实可以不用状态压缩 dp
设 表示前 个数是否有 的倍数
预处理出来每个数变成某个数的倍数要加上多少然后直接转移
int lcm(int a, int b) {return a * b / __gcd(a, b);}
signed main()
{
cin >> n >> x >> y >> z;
for (rint i = 1; i <= n; i++)
{
int t;
cin >> t;
a[i] = x - (t - 1) % x - 1;
b[i] = y - (t - 1) % y - 1;
c[i] = z - (t - 1) % z - 1;
ab[i] = lcm(x, y) - (t - 1) % lcm(x, y) - 1;
bc[i] = lcm(z, y) - (t - 1) % lcm(z, y) - 1;
ac[i] = lcm(x, z) - (t - 1) % lcm(x, z) - 1;
abc[i] = lcm(lcm(x, y), z) - (t - 1) % lcm(lcm(x, y), z) - 1;
}
memset(f, 0x3f, sizeof f);
f[0][0][0][0] = 0;
for (rint i = 1; i <= n; i++)
{
f[i][0][0][0] = f[i - 1][0][0][0];
f[i][0][0][1] = min(f[i - 1][0][0][1], f[i - 1][0][0][0] + c[i]);
f[i][0][1][0] = min(f[i - 1][0][1][0], f[i - 1][0][0][0] + b[i]);
f[i][1][0][0] = min(f[i - 1][1][0][0], f[i - 1][0][0][0] + a[i]);
f[i][0][1][1] = min({f[i - 1][0][1][1], f[i - 1][0][0][1] + b[i], f[i - 1][0][1][0] + c[i], f[i - 1][0][0][0] + bc[i]});
f[i][1][0][1] = min({f[i - 1][1][0][1], f[i - 1][0][0][1] + a[i], f[i - 1][1][0][0] + c[i], f[i - 1][0][0][0] + ac[i]});
f[i][1][1][0] = min({f[i - 1][1][1][0], f[i - 1][1][0][0] + b[i], f[i - 1][0][1][0] + a[i], f[i - 1][0][0][0] + ab[i]});
f[i][1][1][1] = min({f[i - 1][1][1][1], f[i - 1][1][1][0] + c[i], f[i - 1][1][0][1] + b[i], f[i - 1][0][1][1] + a[i], f[i - 1][0][0][1] + ab[i], f[i - 1][0][1][0] + ac[i], f[i - 1][1][0][0] + bc[i], f[i - 1][0][0][0] + abc[i]});
}
cout << f[n][1][1][1] << endl;
return 0;
}
[ARC166C] LU / RD Marking
传送门link
将边转化成点的问题,染色就是染 和 位置的点
将点连边。因为染色方式只有一种,所以这个点阵构成了若干条互不相关的斜向的链。
答案是这些链的方案的乘积。
考虑一条边数为 的链。限制为相邻的边不能同时选。
设 表示前 条边的方案,因为第 条边若不选,则方案为 ,第 条边选不选都无所谓,。若第 条边选,则第 条边一定不能选,所以从 转移过来。初始态为 (即斐波那契数列)
一个矩形切割出的若干条链的边数是形如 这样的。对于左右两边可以求一个 序列奇数位的前缀积,中间部分就快速幂。
复杂度
signed main()
{
f[0] = 1;
f[1] = s[1] = 2;
for (rint i = 2; i < N; i++) f[i] = (f[i - 1] + f[i - 2]) % mod;
for (rint i = 3; i < N; i++)
{
if (!i % 2) continue;
s[i] = s[i - 2] * f[i] % mod;
}
cin >> T;
while (T--)
{
cin >> n >> m;
if (n > m) swap(n, m);
cout << s[2 * n - 1] * s[2 * n - 1] % mod * qpow(f[2 * n], m - n) % mod << endl;
}
return 0;
}
[ARC166D] Interval Counts
传送门link
假设当前已经满足了 的限制,维护一个队列表示当前还没确定右端点的所有左端点。
如果 ,让原来右端点接在 的区间接到
如果 ,原来的区间就算全部接到 上仍然不够,需要再加 个左端点为 的区间。
如果 ,说明要删掉 个区间,由于要让最小值最大,所以需要删掉左端点最小的区间。
于是只维护一个左端点递增的区间,区间数很多,记录这个左端点的个数。
复杂度
int n;
int x[N], y[N];
int ans = inf;
struct node
{
int x, y;
}q[N];
signed main()
{
cin >> n;
for (rint i = 1; i <= n; i++) cin >> x[i];
for (rint i = 1; i <= n; i++) cin >> y[i];
x[0] = -inf;
int head = 1, top = 0;
for (rint i = 1; i <= n; i++)
{
if (y[i] > y[i - 1])
{
q[++top] = {x[i - 1] + 1, y[i] - y[i - 1]};
}
if (y[i] < y[i - 1])
{
int delta = y[i - 1] - y[i];
while(delta && head <= top)
{
int w = min(q[head].y, delta);
delta -= w;
q[head].y -= w;
ans = min(ans, x[i] - 1 - q[head].x);
if (!q[head].y) head++;
else break;
}
}
}
if (ans >= inf) cout << -1 << endl;
else cout << ans << endl;
return 0;
}
[ARC166E] Fizz Buzz Difference
传送门link
在解决这个题目之前,需要学习一个东西。
如何求解线性同余不等式。
给定 ,求最小非负整数 满足:
尝试将问题转化为一个更小规模的问题
其中
如果在 之间找到一个 的倍数,即 ,这意味着 可以取到最小值 。同时,我们直接找到 的一个解:
关于此解的最小性,只看左侧不等式,我们有 是合法解的必要条件 ,即 ,显然此下界关于 是单调递增的,而此解恰好取到了最小下界,故一定是最小的可能合法解,而其合法性显然。
否则,将上式进一步整理为
由于 之间没有 的倍数,设 ,有:
上式对 取模可得:
尝试说明当找到该式中 的最小可行解时就能唯一确定 的最小可行解。首先一个原式中可行的 一定对应一个该式中可行的 ,并且对应关系是单调的,即更小的 对应于不会更大的 ,因此我们找到的最小可行 最小可行 对应的 。同时,对于一个满足上式的 ,容易验证一定有 是原式的可行解,且恰好取到下界。
问题转化为求解 ,即 的新问题,问题规模减小,不断递归调用
Code:
int solve(int l, int r, int a, int m)
{
if (l > r || !a) return -1;
if (!l) return 0;
if (r / a * a >= l) return (l - 1) / a + 1;
int k = solve((a - r % a) % a, (a - l % a) % a, m % a, a);
return k == -1 ? -1 : (m * k + l - 1) / a + 1;
}
现在再来处理这道题目。
假设现在这个合法区间是 ,将 不断 得到一系列区间,它们的 取值一定恰好覆盖 。因此 是 中限制最严格的,将题目限制修改为
合法区间一定满足 ,否则可以将端点外移让区间长度变长且 不会变大。设 ,首先来最大化 ,即最大化
确定 后,,因此需最大化 。
一旦确定区间中第一个 的倍数的位置 ,那么 也唯一确定,故只需要最小化 。
设 ,那么 最小能恰好取到 。因此,确定 后可以直接求得最小可能 ,且此值关于 具有单调性。二分即可。
记求得的最大 为 ,此时区间内至少需要出现 个 的倍数。此限制可以表示为 ,即 ,如果 ,这意味着这条限制不存在,直接令 ;否则问题可以转化成 ,求最小合法 。刚才上面那个代码就可以快速解决。
复杂度 。
int solve(int l, int r, int a, int m)
{
if (l > r || !a) return -1;
if (!l) return 0;
if (r / a * a >= l) return (l - 1) / a + 1;
int k = solve((a - r % a) % a, (a - l % a) % a, m % a, a);
return k == -1 ? -1 : (m * k + l - 1) / a + 1;
}
signed main()
{
int T;
cin >> T;
while (T--)
{
cin >> n >> a >> b;
int g = __gcd(a, b);
int l = 1, r = inf, res;
while (l < r)
{
int mid = (l + r) >> 1;
if (mid - 2 - (a * mid - 1 - g) / b > n) r = mid;
else l = mid + 1, res = mid;
}
int tmp = solve(((1 - a * res) % b + b) % b, b - 1, a, b);
cout << tmp * a + 1 << " " << (tmp + res) * a - 1 << endl;
}
return 0;
}
[ARC166F] Tangent Addition Formula
传送门link
一道不可做的题,看的第一篇题解。懂了但没有完全懂。。。。
的性质二 目前没法入手解决,记标记为式 。
带入 ,得到 ,解得 或 。设 ,即模 意义下虚数单位,则 。
满足 ,说明 时 存在。
由和角公式 ,对比 ,对比可得移了项少了取模。 就是某种模意义下的正切函数。
首先展开 。
根据欧拉公式 ,得到 。
相除得到
形式上 形如
【结论 1】
形如 ,其中 。
证明:将该式带入 ,得到:
利用 ,得到
将分子分母去括号并通分,得到
约分,分解因式,得
拆开分子,得
再代回去
左边同余于右边, 成立。原命题得证。
上面除了 都是恒等变换,因此原问题变成了和 相关的问题。而这个式子,就是二次剩余。
暂记模 意义下的整数域为
讨论
若 ,则可以有 满足
若 ,则可以有 满足
由于这两种数列的取值和项数无关,对于所有 都能这样取
因此,此时 存在。
讨论
【结论 2】
,其中 为奇质数。
证明:
设 的最小正原根为 。由原根存在性定理, 存在。
由 , 可视为 下 次单位根。由 , 可视为 下 次单位根。
因此有 。若 存在,则 ,即
故若 存在,则 ,必要性得证。
对于充分性,取 ,满足题意。
原命题得证。
既然如此,再结合上 ,我们继续分类。
接下来。所有结论都有前提条件 。
讨论
【结论 3】
若 ,则
数学归纳法,当 时显然相等。
现在假设当 时成立,即 ,下证当 时成立。
代入 ,得
代入 ,两边同时除以 ,得到
原命题得证
由此可得,本类的合法 有且仅有六种情况
讨论
【结论 4】
若 ,则
证明:考虑证明其逆否命题,即若 ,则
将 代入 ,得到 。
化简得到 。
因为 ,两边同时除以 ,移项得
解得
原命题得证
将特值代入 也起不了效果了,只能求助结论 1 了
由于分母不能为 ,所以要求 。如果存在 ,则 存在,否则 不存在
由性质三:。列方程
化简得到 ,是个模为质数的高次剩余方程,原根加 BSGS 就能解
在本分类下, 存在当且仅当存在 使得 。
讨论
在普通的模意义下整数域上没有
但是高斯整环 ???
不妨记模 意义下高斯整环为 ,不难得出这玩意是个有限域。
在强行添加上虚数单位后,讨论 的存在条件。
由于对于所有 可以看作 ,因此
依题有 ,因此 应该满足 且 。
判断属不属于集合还是太麻烦了。能不能转化一下?当然可以。我们有如下结论:
【结论 5】
证明:根据二项式定理有 。(仅有 和 不能被 整除,其余均含因数 。)
设 ,则
由于 ,有 ,因此
根据费马小定理,,所以
当且仅当 ,即 时,
故
代入 ,原命题可转化为
移项有
拆括号,得到
两边同乘 ,得:
用上面二项式定理的推论展开幂次,得
通分,得
去分母,移项,得
由于该式对于所有 均成立,可以去掉指数,得
充分性得证。以上各式均为恒等变换,反过来可证明必要性 原命题得证。
和上面同样解方程 ,解得
不知道原根存在性,没办法 BSGS
是个有限域吗
这个域共有 个元素,根据有限域的存在性和唯一性,其为有限域
【结论 6】
(有限域原根存在性)
有限域 存在元素 使得
综合上述讨论,我们最终得到 的存在性判定方式
当 或 或 时, 存在
否则,若 存在,则存在 满足以下三个条件
其中 。
记循环群大小
设 ,则 。
由 ,得到 ,因此有
同理,由 得
因此 。这玩意可以继续除下去直到 中的 因数被除干净
设 ,则可以推出
所以 的一个取值是
不妨就令 为该值。则再设
还差条件一,代入 得
再和上面一样的做法,得出
复杂度
int qpow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1) res = res * a % p;
b >>= 1;
a = a * a % p;
}
return res;
}
struct complex
{
int X, Y;
bool operator == (complex x) {return X == x.X && Y == x.Y;}
complex operator * (complex x)
{
complex s;
s = (complex) {X * x.X % p + imod * x.Y % p * Y % p, Y * x.X % p + X * x.Y % p};
if (s.X >= p) s.X -= p;
if (s.Y >= p) s.Y -= p;
return s;
}
complex operator ^ (int b)
{
complex s({1, 0}), a(*this);
while (b) {if(b & 1) s = s * a;a = a * a;b >>= 1;}
return s;
}
};
signed main()
{
cin >> T;
while (T--)
{
cin >> p >> a >> b;
if (p == 2 || b == 0 || b * b % p + 1 == p)
{
puts("Yes");
continue;
}
if ((p & 3) == 1)
{
n = p - 1;
int f, l;
do
{
l = rand() % p;
imod = l * l % p - (p - 1);
imod < 0 && (imod += p);
}
while (!l || qpow(imod, (p - 1) >> 1) == 1);
f = ((complex) {l, 1} ^ ((p + 1) >> 1)).X;
if (p - f < f) f = p - f;
int t = n / (n & (-n));
t /= __gcd(a, t);
int u = (f - b) * qpow(f + b, p - 2) % p;
if (u < 0) u += p;
if (qpow(u, t) == 1) puts("Yes");
else puts("No");
}
else
{
n = p * p - 1;
imod = p - 1;
int t = n / ((n & (-n)) / __gcd((n & (-n)), p - 1) * (p - 1));
t /= __gcd(a, t);
int inv = qpow(b * b % p + 1, p - 2);
complex u = {(1 - b * b % p + p) * inv % p, (b << 1) % p * inv % p};
if ((u ^ t) == (complex){1, 0}) puts("Yes");
else puts("No");
}
}
return 0;
}
本文作者:PassName
本文链接:https://www.cnblogs.com/spaceswalker/p/17976480
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步