2020.11.4 Touhou 欢乐赛
写在前面
题目及数据下载:链接:https://pan.baidu.com/s/1ZXLx8HGKgIicCCv7MwFukg
提取码:koko
除了 B 题目都是搬的:
A CF1428C ABBB
C CF1444A Division
D P3786 萃香抱西瓜
在这里感谢原出题人,感谢验题人 Kersen,yu__xuan,eee_hoho。
比较亲民的一场。
无脑暴力分并不是很多,涉及的算法都很简单,主要考察阅读理解能力。
A 大空魔术
「哈啊,宇宙还真是充满魅力啊……」
「怎么了?发出那种叹息。你的意思是地上已经毫无魅力了吗?」
「16时31分。看到长庚星了啊。因为地上几乎已经没有不可思议的事情了呢」
「如果像莲子这样能够看到这个世界的全部构造,那么心里的虚无主义就会开始露头了啊」
《大空魔术 ~ Magical Astronomy.》
有一个只包含 'A' 与 'B' 的字符串,每次可以消掉一个 "AB" 或一个 "BB",并把剩下的拼在一起。
求字符串最短的长度。
注意到可以消去 "AB","BB" 即可以消去 "?B"。
最后字符串的形态一定是 "BAA...A" 或者 "AA...AA"。
想到括号序列,考虑使用一个栈模拟该过程。
如果当前加入的是 "B" 且栈非空,则弹出栈顶元素,否则加入该字符。
因为能删去的只有 "?B",所以能删就删最优。
总复杂度 \(O(t|s|)\)。
这种情况:"ABB",显然应先删去 "AB",可以发现上面的模拟过程也符合这样的删除策略。
//知识点:贪心,模拟
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stack>
#define LL long long
const int kMaxn = 2e6 + 10;
//=============================================================
int T, n, ans;
char s[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Chkmax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
if (sec_ < fir_) fir_ = sec_;
}
//=============================================================
int main() {
T = read();
while (T --) {
std::stack <char> st;
scanf("%s", s + 1);
n = strlen(s + 1), ans = 0;
for (int i = 1; i <= n; ++ i) {
if (s[i] == 'A') {
st.push('A');
} else if (! st.empty()) {
st.pop();
} else {
st.push('B');
}
}
for (; ! st.empty(); st.pop()) {
ans ++;
}
printf("%d\n", ans);
}
return 0;
}
B 夜桜街道
给定一长度为 \(n\) 的数列 \(a\),定义 \(f(l,r)\),有:
\[f(l,r) = \sum_{i=l}^{r}\sum_{j=i+1}^{n}[a_i< a_j] \]其中 \([\ ]\) 表示 艾佛森括号。
求:\[\sum_{i=1}^{n}\dfrac{f(1,i)}{i} \pmod {998244353} \]
发现给定的 \(f(l,r)\) 即为区间 \([l,r]\) 内的顺序对个数。
考虑如何通过 \(\sum_{i=1}^{n-1} f(1,i)\) 得到 \(\sum_{i=1}^{n} f(1,i)\)。
发现新增的贡献即为较大的数为 \(a_n\) 的顺序对。
离散化后顺序枚举数列,树状数组维护,统计答案时乘上一个逆元即可。
模数是质数,逆元怎么搞都行,总复杂度均为 \(O(n\log n)\)。
//鐭ヨ瘑鐐癸細鏍戠姸鏁扮粍锛岄€嗗厓
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kMaxn = 1e6 + 10;
const LL mod = 998244353;
//=============================================================
int n, d_num, a[kMaxn], data[kMaxn];
LL f, ans, inv[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Chkmax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
if (sec_ < fir_) fir_ = sec_;
}
LL QuickPow(LL x_, LL y_) {
LL ret = 1;
for (; y_; y_ >>= 1ll) {
if (y_ & 1ll) ret = ret * x_ % mod;
x_ = x_ * x_ % mod;
}
return ret;
}
namespace Bit {
#define lowbit(x) (x&-x)
int Lim, t[kMaxn];
void Init(int lim_) {
Lim = lim_;
}
void Insert(int pos_) {
for (int i = pos_; i <= Lim; i += lowbit(i)) {
t[i] ++;
}
}
int Sum(int pos_) {
int ret = 0;
for (int i = pos_; i; i -= lowbit(i)) {
ret += t[i];
}
return ret;
}
}
void Prepare() {
n = read();
inv[1] = 1;
for (int i = 1; i <= n; ++ i) {
data[i] = a[i] = read();
if (i > 1) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
std::sort(data + 1, data + n + 1);
d_num = 1;
for (int i = 2; i <= n; ++ i) {
if (data[i] != data[i - 1]) ++ d_num;
data[d_num] = data[i];
}
for (int i = 1; i <= n; ++ i) {
a[i] = std::lower_bound(data + 1, data + d_num + 1, a[i]) - data;
}
Bit::Init(d_num);
}
//=============================================================
int main() {
Prepare();
for (int i = 1; i <= n; ++ i) {
f = (f + 1ll * Bit::Sum(a[i] - 1)) % mod;
ans = (ans + f * inv[i] % mod) % mod;
Bit::Insert(a[i]);
}
printf("%lld\n", ans);
return 0;
}
C 科学世纪
梦违,幻之红屋里闪烁的异彩
现世,构筑于毫无血色的石块之上
空想的梦,描绘了古老的美丽都市的童话
白日,照在越发肮脏的街市里
《梦违科学世纪》
\(t\) 组数据。
给定参数 \(p,q\),求一个最大的 \(x\),满足 \((x|p) \land (q\nmid {x})\)。
\(1\le t\le 500\),\(1\le p\le 10^{18}\),\(2\le q\le 10^9\)。
1S,512MB。
显然 \(p \bmod q \not= 0\),输出 \(p\)。
当 \(p \bmod q = 0\) 时,有个想法。
想到让 \(p\) 除以某些 \(q\) 的质因子,直到 \(p\) 不能被 \(q\) 整除。
\(p\) 中比 \(q\) 大的质因子没有影响,可以仅考虑 \(q\) 的质因子。
并且仅削除一种质因子,肯定比削除多种质因子优。
于是枚举 \(q\) 的每一种质因子,令 \(p\) 除以该质因子,直到 \(p\) 中该质因子的次数比 \(q\) 中的次数 小。
统计削除不同质因子时,答案的最大值即可。
总复杂度 \(O(t\sqrt{q})\)。
先对 \(p,q\) 质因数分解,设 \(\{a_i\}\) 为质数集,\(\{b_i\}\) 为对应质数的次数:
有 \((x|p) \land (q\nmid {x})\),则 \(x\) 质因数分解后有:
第二个条件表示 \(x|p\),第三个条件表示存在一个整除 \(q\) 的质数 \(a_j\),它在 \(x\) 中的次数比在 \(q\) 中的次数要小,从而使 \(q\nmid x\)。
显然,最优的 \(x\) 一定是 \(p\) 抠去一些质因子 \(a_j\),使得该质因子在 \(p\) 中的次数小于 \(q\) 中的次数后剩下的值。
显然抠去的质因子最多有一个。
所以可以枚举 \(q\) 的所有质因子并进行计算。
感谢 @caq 指出错误并提供了一份好看的代码。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define int long long
using namespace std;
int T;
signed main()
{
cin>>T;
while(T--)
{
int ans=1;
int p,q;
cin>>p>>q;
int kap=p;
if(p<q)
{
cout<<p<<endl;
continue;
}
if(p%q!=0)
{
cout<<p<<endl;
continue;
}
int cun=q;
for(int i=2;i*i<=cun;i++)
{
if(q%i==0ll)
{
int cntq=0;
int cntp=0;
while(q%i==0ll)
{
q/=i;
cntq++;
}
while(p%i==0ll)
{
p/=i;
cntp++;
}
if(cntp<cntq) continue;
int maxn=kap;
for(int k=cntp-cntq+1;k;k--)
{
maxn/=i;
}
if(maxn>ans)
{
ans=maxn;
}
}
}
if(q!=1)
{
int cntp_=0;
while(p%q==0ll)
{
p/=q;
cntp_++;
}
int pd=kap;
for(int k=cntp_;k;k--)
{
pd/=q;
}
if(pd>ans) ans=pd;
}
cout<<ans<<endl;
}
}
D 百鬼夜行
伊人不见隐何方,
吹散迷雾落月光,
萃取盛宴集佳酿,
香气溢满堂。
无法简述的题面。
其实根本没有 -1 的点
稍有点细节的状压 DP。
读入时对所有时刻所有位置进行标记,记录是否有大小西瓜。
发现小西瓜个数较小,考虑状压一下获得的小西瓜状态。
设 \(s_{t,x,y}\) 表示 \(t\) 时刻,位置 \((x,y)\) 的小西瓜的存在状态。
设 \(f(t, x, y, S)\) 表示,在时间 \(t\),萃香的位置在 \((x,y)\),获得的小西瓜状态为 \(S\) 时,需要移动的最小步数。
初始化 \(f(1, sx, sy, s_{1,sx,sy}) = 0\),所有不可到达状态的 \(f = \operatorname{Inf}\)。
转移时枚举每个时刻每个位置,从能到达该位置的上个时刻的位置 \((x',y')\) 转移过来,还需要枚举上个位置的小西瓜状态,有:
统计答案时枚举最后一个时刻的所有取完所有小西瓜的状态,取最小值即可。
复杂度 \(O(Thw2^m)\),数据范围小可以随便过。
//知识点:分层图,状态压缩,最短路,DP
/*
By:Luckyblock
注意数组大小
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kMaxH = 5 + 1;
const int kMaxn = 20 + 1;
const int kMaxT = 100 + 1;
const int kMaxAll = (1 << 10) + 1;
const int kInf = 0x3f3f3f3f;
const int ex[5] = {0, 0, 0, 1, -1};
const int ey[5] = {0, 1, -1, 0, 0};
//=============================================================
int h, w, T, sx, sy;
int n, m, all, t1[kMaxn], t2[kMaxn];
int f[kMaxT][kMaxH][kMaxH][kMaxAll];
int s_num, s[kMaxT][kMaxH][kMaxH];
//=============================================================
inline int read() {
int f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Chkmax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
if (sec_ < fir_) fir_ = sec_;
}
void Prepare() {
h = read(), w = read(), T = read();
sx = read(), sy = read();
n = read(), m = read();
for (int i = 1; i <= n; ++ i) {
t1[i] = read(), t2[i] = read();
int type = read();
if (type) s_num ++;
for (int j = t1[i]; j < t2[i]; ++ j) {
int x = read(), y = read();
s[j][x][y] = type ? (1 << (s_num - 1)) : -1;
}
}
}
//=============================================================
int main() {
Prepare();
if (s[1][sx][sy] == -1) {
printf("-1");
return 0;
}
all = (1 << m) - 1;
memset(f, 0x3f, sizeof (f));
f[1][sx][sy][s[1][sx][sy]] = 0;
for (int i = 2; i <= T; ++ i) {
for (int x = 1; x <= w; ++ x) {
for (int y = 1; y <= h; ++ y) {
if (s[i][x][y] == -1) continue ;
for (int j = 0; j <= 4; ++ j) {
int fx = x + ex[j], fy = y + ey[j]; //from
if (fx < 1 || fx > w || fy < 1 || fy > h) continue ;
if (s[i - 1][fx][fy] == -1) continue ;
for (int k = 0; k <= all; ++ k) {
Chkmin(f[i][x][y][k | s[i][x][y]],
f[i - 1][fx][fy][k] + (j > 0));
}
}
}
}
}
int ans = f[0][0][0][0];
for (int x = 1; x <= w; ++ x) {
for (int y = 1; y <= h; ++ y) {
Chkmin(ans, f[T][x][y][all]);
}
}
printf("%d\n", ans < kInf ? ans : -1);
return 0;
}