牛客多校2024-8
K - Haitang and Ava
怎么还有人签到题wa啊/kk
定义以下的字符串是合法的:
- 空字符串
- 若\(S\)是合法的,那么\(S\)+
ava
和ava
+\(S\)都是合法的 - 若\(S\)是合法的,那么\(S\)+
avava
和avava
+\(S\)都是合法的
给定一个字符串,判断是否合法。
以v为分隔计数a,容易发现计数数组中除了开头和末尾不能有两个1相邻,2可以任意。
(然后发现可以直接从开头截取avava
或者ava
,前者优先,简单了114514倍)
int T = next();
while(T--)
{
string str; cin>>str;
bool no = 0;
for(auto ch: str) if (ch != 'a' && ch != 'v') no = 1;
if (no) { cout<<"No"<<endl; continue; }
int cnt = 0;
vector<int> a;
rep(i, 0, str.size())
{
if (str[i] == 'a') cnt++;
else a.push_back(cnt), cnt = 0;
}
if (cnt != 1 || a[0] != 1) no = 1; // 开头或末尾a个数>=2
if (no) { cout<<"No"<<endl; continue; }
a.erase(a.begin());
rep(i, 0, a.size())
{
if (i && a[i] == a[i-1] && a[i] == 1) no = 1;
if (!a[i] || a[i] > 2) no = 1; // 连续的v个数>1或连续的a个数>2
}
if (no) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
int T = next();
while(T--)
{
string str; cin>>str;
bool no = 0;
int i = 0;
while(i < str.size())
{
if (str[i] == 'a' && str[i+1] == 'v' && str[i+2] == 'a')
{
if (i+4 < str.size() && str[i+3] == 'v' && str[i+4] == 'a') i += 5;
else i += 3;
}
else { no = 1; break; }
}
if (no) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
A - Haitang and Game
Alice 和 Bob 对于集合\(a\)中的数玩一个游戏。Alice 先行。一次行动为:
对于\(x, y \in S\)并且\(gcd(x, y) \notin S\),将\(gcd(x,y)\)插入\(S\)。
没法行动的人输掉。理想情况下输出胜者。
\(T \leq 20,n \leq 1e5,a_i \leq 1e5\)
首先发现插入\(gcd(x,y)\)并不能让另一个玩家有更多操作选项,因此不存在博弈。
问题从而转化成了求\(gcd(x,y) \notin S\)的数量,从而得出奇偶。
思考一会儿发现,单从\(a_i\)的质因数讨论可能的\(gcd\)困难重重。观察到\(a_i \leq 1e5\),可以直接从值域入手。考虑一个数\(t \notin a\),记\(a'=\{kt\ |\ k \in \mathbb{N_+},kt \in a\}\),那么\(gcd(a')=pt(p \in \mathbb{N_+})\)。如果\(p=1\),则\(t\)会被加入进\(a\)。枚举\(t\)模拟上述过程即可。
int T = next();
while(T--)
{
memset(vis, 0, sizeof(vis));
int n = next();
rep(i, 0, n) cin>>w[i], vis[w[i]] = 1;
int cnt = 0;
hrp(i, 1, 1e5) if (!vis[i])
{
vector<int> v;
for(int j = i; j <= 1e5; j += i) if (vis[j]) v.push_back(j);
if (v.empty()) continue;
int gcd = v[0];
rep(i, 1, v.size()) gcd = __gcd(gcd, v[i]);
if (gcd == i) cnt++;
}
if (cnt%2) cout<<"dXqwq"<<endl;
else cout<<"Haitang"<<endl;
}
E - Haitang and Math
定义\(S(m)\)为\(m\)所有数位和。给定\(n\),求满足\(m \leq n\)且\(n\mod m=S(m)\)的\(m\)个数。
\(n \leq 1e12\)
因为\(n \leq 1e12\),所以\(S(m) \leq 108\)。从而将条件转化为\(n-S(m) \mod m = 0\)。
考虑枚举\(S(m)\)的值,可以得到集合\(R=[n-108, n-1]\)。再判断\(R\)中元素的因数是否等于当前\(S(m)\)的值即可。
然而不幸的是,对于\(R\)中每个元素求质因数会TLE
。因此需要优化,从而引出了区间筛这个东西(我也不知道这是什么?)。
对于每个质数\(p \leq sqrt(n)\),我们只需要考虑它们在\(R\)中的倍数即可。因此有两种方法:
- 如果\(n\%p > 108\),它不可能是\(R_i\)的因数,因此可以直接跳过。
- 只枚举\(R\)中\(p\)的倍数。
实测两种方法都跑进了200ms,第一种略快。代码采用的是第一种。
const int U = 1e6+500;
bool isPrime[U];
vector<int> prime;
void Eratosthenes(int n)
{
isPrime[0] = isPrime[1] = 0;
hrp(i, 2, n) isPrime[i] = 1;
hrp(i, 2, n) if (isPrime[i])
{
prime.push_back(i);
for(int j = i; j <= n; j += i) isPrime[j] = 0;
}
}
vector<pair<int, int>> fac[120];
vector<int> fs[120];
int ms[120];
int S(int e)
{
int ans = 0;
while(e) ans += e%10, e /= 10;
return ans;
}
void dfs(int p, int e, int num)
{
if (e == fac[p].size())
{
fs[p].push_back(num);
return;
}
dfs(p, e+1, num);
hrp(i, 1, fac[p][e].second) dfs(p, e+1, num *= fac[p][e].first);
}
Eratosthenes(1e6+100);
int T = next();
while(T--)
{
int n = next(), cnt = 0;
hrp(i, 1, 108) ms[i] = n-i, fs[i].clear(), fac[i].clear();
for(int i = 0; prime[i]*prime[i] <= n; i++)
{
if (prime[i] < 108) hrp(j, 1, 108)
{
if (ms[j] < prime[i]) continue;
int cnt = 0;
while(ms[j]%prime[i] == 0) ms[j] /= prime[i], cnt++;
if (cnt) fac[j].push_back({prime[i], cnt});
}
else
{
int j = n%prime[i], cnt = 0;
if (j > 0 && j <= 108) while(ms[j]%prime[i] == 0) ms[j] /= prime[i], cnt++;
if (cnt) fac[j].push_back({prime[i], cnt});
}
}
hrp(i, 1, 108) if (ms[i] > 1) fac[i].push_back({ms[i], 1});
hrp(i, 1, 108) if (ms[i] > 0) dfs(i, 0, 1);
hrp(i, 1, 108) for(auto v: fs[i]) if (v != i && S(v) == i) cnt++;
cout<<cnt<<endl;
}
J - Haitang and Triangle
给定\(n,m\),构造一个满足以下条件的排列,或输出无解:
恰好有\(m\)个长度为\(3\)的子区间,使得这个子区间内的三个数能构成三角形。
显然:\(1,2,3,4,5,...\)是使\(m\)最大的排列。
这道题的关键在于观察到\(m=0\)的排列如何构成,锻炼观察力从现在做起!
观察到如果\(n\)是\(3\)的倍数,记\(d=n/3\),那么\(d,2d,3d,d-1,2d-1,3d-1,d-2,...,d+1,2d+1\)是\(m=0\)时的一种排列。考虑扩增这个排列,那么\(3d+1\)可以放在左边,\(3d+2\)可以放在右边,分别对应了\(n=3k+1\)和\(n=3k+2\)的情况。
再考虑\(m \neq 0\)的情况,可以将\(a=n-m\)先按上述过程排列好。再将剩下\(m\)个数全都排列在排列右边。这种方法对于\(n=3k\)(\(3d+1 > d+1+2d+1\))和\(n=3k+2\)(\(3d+3>3d+2+2d+1\))的情况都成立,但对于\(n=3k+1\)(\(3d+2 = 2d+1+d+1\))的情况不成立。因此当\(m \neq 0\)的时候可以将\(3d+1\)和\(3d+2\)换位后再将剩下\(m\)个数全都排列在排列右边。
int T = next();
while(T--)
{
int n, m;
cin>>n>>m;
if (m >= n-2) { cout<<-1<<endl; continue; }
int a = n-m, d = a/3;
deque<int> dq;
rev(i, d, 1) dq.push_back(i), dq.push_back(i+d), dq.push_back(i+2*d);
if (a%3 == 1)
{
if (!m) dq.push_front(a);
else
{
dq.push_back(a);
dq.push_front(a+1);
hrp(i, a+2, n) dq.push_back(i);
}
}
else
{
if (a%3 == 2) dq.push_front(a-1), dq.push_back(a);
hrp(i, a+1, n) dq.push_back(i);
}
for(auto v: dq) cout<<v<<' ';
cout<<endl;
}
D - Haitang and Uma Musume
非常困难的题目,当然题目指题面
进行一个赛马娘训练的模拟。
一个坑:只有summer
值等于\(1\)的训练需要计数。
const double eps = 1e-6;
const int base[5][5][6] = {{{10, 0, 5, 0, 0, 2}, {0, 9, 0, 4, 0, 2}, {0, 5, 8, 0, 0, 2}, {4, 0, 4, 8, 0, 2}, {2, 0, 0, 0, 9, 4}},
{{11, 0, 5, 0, 0, 2}, {0, 10, 0, 4, 0, 2}, {0, 5, 9, 0, 0, 2}, {4, 0, 4, 9, 0, 2}, {2, 0, 0, 0, 10, 4}},
{{12, 0, 5, 0, 0, 2}, {0, 11, 0, 4, 0, 2}, {0, 6, 10, 0, 0, 2}, {4, 0, 4, 10, 0, 2}, {3, 0, 0, 0, 11, 4}},
{{13, 0, 5, 0, 0, 2}, {0, 12, 0, 4, 0, 2}, {0, 6, 11, 0, 0, 2}, {4, 0, 4, 11, 0, 2}, {3, 0, 0, 0, 12, 4}},
{{14, 0, 5, 0, 0, 2}, {0, 13, 0, 4, 0, 2}, {0, 7, 12, 0, 0, 2}, {5, 0, 5, 12, 0, 2}, {4, 0, 0, 0, 13, 4}}};
int cnt[10];
double coe[10] = {-0.2, -0.1, 0, 0.1, 0.2};
// base[lv][type][X]
struct SupportCard
{
int friendd, drive, train;
int initial[20], bonus[20];
void input(void)
{
cin>>friendd>>drive>>train;
rep(i, 0, 5) cin>>initial[i];
rep(i, 0, 5) cin>>bonus[i];
}
} sc[10];
struct UmaMusume
{
int attribute[20];
int bonus[20];
void input(void)
{
rep(i, 0, 5) cin>>attribute[i];
rep(i, 0, 5) cin>>bonus[i];
}
void check(void)
{
rep(i, 0, 5) attribute[i] = min(attribute[i], 1200LL);
}
void init(void)
{
attribute[5] = 120;
rep(i, 0, 5) rep(j, 0, 6) attribute[i] += sc[j].initial[i];
check();
}
} uma;
struct Training
{
int summer, weight, drive, type, lv, num;
int card[20], friendd[20];
void input(void)
{
cin>>summer>>weight>>drive>>type>>num;
rep(i, 0, num) cin>>card[i]>>friendd[i], card[i]--;
}
void level(void)
{
if (summer) lv = 4;
else lv = min(4LL, cnt[type]/4);
}
double stimulate(int ability)
{
if (weight && !ability) return 0;
level();
double delta[10] = {base[lv][type][ability], 1, 1, 1, 1, 1};
int sumPresentBonus = 0;
rep(i, 0, num) sumPresentBonus += sc[card[i]].bonus[ability];
delta[0] += sumPresentBonus;
rep(i, 0, num) if (friendd[i]) delta[1] *= (1+0.01*sc[card[i]].friendd);
int sumPresentTrain = 0;
rep(i, 0, num) sumPresentTrain += sc[card[i]].train;
delta[2] += 0.01*sumPresentTrain;
int sumPresentDrive = 0;
rep(i, 0, num) sumPresentDrive += sc[card[i]].drive;
delta[3] += coe[drive]*(1+0.01*sumPresentDrive);
delta[4] += 0.01*uma.bonus[ability];
delta[5] += 0.05*num;
double res = 1;
rep(i, 0, 6) res *= delta[i];
return res;
}
} tr[100];
uma.input();
rep(i, 0, 6) sc[i].input();
int n = next();
rep(i, 0, n) tr[i].input();
uma.init();
rep(i, 0, n)
{
rep(j, 0, 6)
{
int t = tr[i].stimulate(j);
uma.attribute[j] += t+eps;
}
uma.check();
if (!tr[i].summer) cnt[tr[i].type]++;
rep(j, 0, 6) cout<<uma.attribute[j]<<' ';
cout<<endl;
}
G - Haitang and Rock Paper Scissors
dp套dp,咕咕咕
I - Haitang and Ranking
三维偏序,但单点修改第三维。只会cdq的有福了