好难的一场Div.4,本来想着随便打,然后比赛开始的时候还没到家,在路上开了G和C题
太困了没写完,明天继续
A. Skibidus and Amog’u
题意:把一个字符串最后的ususus改为iii
输入前|S|-2个字符,再输出一个iii
void solve() {
string s;cin>>s;
for(int i = 0;i<s.size()-2;i++){
cout<<s[i];
}
cout<<"i"<<endl;
}
B. Skibidus and Ohio
题意:给出一个字符串,可以执行任意次操作,每次操作可以将两个相邻且相同的字符替换为任意字符,求该字符串可以达到的最小长度
只要一个字符串可以执行一次操作,那么将其替换成前一个或后一个字符就可以一直操作下去直到变为1,否则长度不变
void solve() {
string s;cin>>s;
for(int i = s.size()-1;i>0;i--){
if(s[i] == s[i-1]){
cout<<1<<endl;
return;
}
}
cout<<s.size()<<endl;
}
C2. Skibidus and Fanum Tax (hard version)
题意:有a和b两个数组,长度分别为n和m,对于a中的每个数都可以执行仅1次如下操作:在m中选择一个数字,将aia_iai变为bj−aib_j-a_ibj−ai,求是否可以将数组A变为非递减排序
C1我没看,应该是不用二分直接暴力就可以,就不写了
首先,贪心的想,从前往后遍历数组a,如果可以将其变为一个更小的数,那么一定不将其变为较大的数,那么首先先将第一个数变为可以达到的最小值为min(a[0]−b[0],a[0])min(a[0]-b[0],a[0])min(a[0]−b[0],a[0]),b[0]表示b中最小的数,然后去遍历a中的每个数,将其变为比a[i-1]大的最小值,具体操作为:b[j]−a[i]>=a[i−1]b[j]-a[i] >=a[i-1]b[j]−a[i]>=a[i−1]可以写为b[j]>=a[i−1]+a[i]b[j]>=a[i-1]+a[i]b[j]>=a[i−1]+a[i],不等式后面的值是确定的,所以我们要做的是在数组b中找到第一个大于等于a[i−1]+a[i]a[i-1]+a[i]a[i−1]+a[i]的值,直接用二分查找找这样的一个值就行,然后有四种情况,代码写的很乱,就是(不)需要修改和(不)能找到满足的b[j]b[j]b[j],这四种情况,然后判断一下就行。我用的mi去表示当前处理过的最小值
void solve() {
int n,m;cin>>n>>m;
vector<int> a(n),b(m);
for(int i = 0;i<n;i++){
cin>>a[i];
}
for(int i = 0;i<m;i++){
cin>>b[i];
}
sort(b.begin(),b.end());
auto check = [&](int x){
int l = 0,r = m-1;
while(l<r){
int mid = (l+r)>>1;
if(b[mid]>=x) r = mid;
else l = mid+1;
}
if(b[l]<x) return -1;
else return l;
};
int mi = min(a[0],b[0]-a[0]);
for(int i = 1;i<n;i++){
int ans = mi+a[i];
int tmp = check(ans);
if(tmp == -1) ans = INT_MIN;
else ans = b[tmp]-a[i];
if(ans<mi && a[i]<mi){
cout<<"NO"<<endl;
return;
}
else{
if(ans<mi){
mi = a[i];
}
else if(a[i]<mi){
mi = ans;
}
else{
mi = min(ans,a[i]);
}
}
}
cout<<"YES"<<endl;
}
D. Skibidus and Sigma
题意:给你n个长度为m的数组,将这n个数组按照任意顺序连接成一个长度为n∗mn*mn∗m的数组b,使得这个数组的分数∑i=1n∗m∑j=1ibj\sum_{i = 1}^{n*m}\sum_{j = 1}^{i}b_j∑i=1n∗m∑j=1ibj的值最大
看到按照任意顺序想到是一个排序问题,那么应该怎么排才能使得答案最大呢?
可以想到每个数对分数的贡献为ai∗(∣a∣−i+1)a_i*(|a|-i+1)ai∗(∣a∣−i+1),∣a∣|a|∣a∣表示数组a的长度,那么把大的数排在前面一定是没错的,但由于只能对n个数组排序,不能对每个数组的内部元素进行排序,所以应该是尽可能总和大的数组排在前面是更优的,那么又万一两个数组元素不同但总和相同,这时候应该如何排。
其实到这里举了好多例子也没发现有啥规律,后来想到按照排序的做法想一下
对于数组A和B,设其长度均为n,和表示为sumA,sumBsum_A,sum_BsumA,sumB,计算一下AB拼接和BA拼接的分数
cnt(AB)=∑i=1n(2n−i+1)∗ai+∑i=1n(n−i+1)bi
cnt(AB) = \sum_{i = 1}^{n}(2n-i+1)*a_i+\sum_{i = 1}^n(n-i+1)b_i
cnt(AB)=i=1∑n(2n−i+1)∗ai+i=1∑n(n−i+1)bi
提一个naina_inai出来,即
cnt(AB)=∑i=1nn∗ai+∑i=1n(n−i+1)∗ai+∑i=1n(n−i+1)bicnt(AB)=n∑i=1nai+∑i=1n(n−i+1)∗ai+∑i=1n(n−i+1)bi
cnt(AB) = \sum_{i = 1}^{n}n*a_i+\sum_{i = 1}^{n}(n-i+1)*a_i+\sum_{i = 1}^n(n-i+1)b_i\\
cnt(AB) = n\sum_{i = 1}^{n}a_i+\sum_{i = 1}^{n}(n-i+1)*a_i+\sum_{i = 1}^n(n-i+1)b_i
cnt(AB)=i=1∑nn∗ai+i=1∑n(n−i+1)∗ai+i=1∑n(n−i+1)bicnt(AB)=ni=1∑nai+i=1∑n(n−i+1)∗ai+i=1∑n(n−i+1)bi
观察一下可以发现n∑i=1nain\sum_{i = 1}^{n}a_in∑i=1nai就是sumasum_asuma,而后面两项分别为A,B单独时的分数
然后再同理计算一下cnt(BA)=n∑i=1nbi+∑i=1n(n−i+1)bi+∑i=1n(n−i+1)∗aicnt(BA) = n\sum_{i =1}^nb_i+\sum_{i = 1}^n(n-i+1)b_i+\sum_{i = 1}^{n}(n-i+1)*a_icnt(BA)=n∑i=1nbi+∑i=1n(n−i+1)bi+∑i=1n(n−i+1)∗ai
可以发现,其实只和放在前面的数组的和有关,和内部元素是什么样的没关系,解决了刚刚想的,万一有两个数组和一样怎么办的疑问,所以排序方式即为按照总和的大小从大到小去链接即可。
void solve() {
int n,m;cin>>n>>m;
vector<vector<i64>> a(n,vector<i64>(m));
for(int i = 0;i<n;i++){
i64 sum = 0;
for(int j = 0;j<m;j++){
cin>>a[i][j];
sum+=a[i][j];
}
a[i].pb(sum);
}
sort(a.begin(),a.end(),[](vector<i64> a,vector<i64>b){
return a.back()>b.back();
});
i64 ans = 0;
i64 pre = 0;
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
pre+=a[i][j];
ans+=pre;
}
}
cout<<ans<<endl;
}
E. Skibidus and Rizz
题意:将一个01字符串的平衡值定义为∣0的个数−1的个数∣|0的个数-1的个数|∣0的个数−1的个数∣,给出n表示0的个数,m表示1的个数,用这些01组成一个字符串使得其所有子串的平衡值最大为k,如果不存在这样的字符串,输出-1
很简单可以想到,平衡值最大的情况为所有1放一边,所有0放一边,此时子串中的平衡值最大为max(n,m)max(n,m)max(n,m),所以当k>max(n,m)k>max(n,m)k>max(n,m)时,不存在满足题意的字符串
然后因为所有0和1都要用上,所以不论怎么构造,最终字符串的平衡值为∣n−m∣|n-m|∣n−m∣,即如果k<∣n−m∣k<|n-m|k<∣n−m∣的话,无论怎么构造也不会出现满足题意得字符出串。
构造方式为:
假设n<m,先放k个1,之后01交替地放置字符,直到某个字符个数为0停止,此时剩余的字符个数在放置的时候计算一下,然后在最后将剩余的字符全部放在末尾。
在m>n的时候,按照n<m的方式构造字符串,最后再将01反值即可
void solve() {
int n,m,k;
cin>>n>>m>>k;
if(k>max(n,m) || k<abs(n-m)){
cout<<"-1"<<endl;
return;
}
int f = 0;
if(n>m){
f = 1;swap(n,m);
}
vector<char> a;
for(int i = 0;i<k;i++){
a.pb('1');
m--;
}
int tmp = 0;
while(n && m){
if(tmp == 0){
a.pb('0');
tmp = 1;n--;
}
else{
a.pb('1');
m--;tmp = 0;
}
}
if(n>k || m>k){
cout<<"-1"<<endl;
return;
}
if(n){
while(n){
a.pb('0');
n--;
}
}
else{
while(m){
a.pb('1');
m--;
}
}
for(int i = 0;i<a.size();i++){
if(f == 1){
if(a[i] == '0') cout<<'1';
else cout<<'0';
}
else{
cout<<a[i];
}
}
cout<<endl;
}
G.Skibidus and Capping
题意:给出一个数列,求其中有多少组数的lcmlcmlcm为半素数,半素数为有且仅有两个质因子
这个题想了太久了,当时看题的时候还在路上,也不记得最开始是怎么一步步想的了
其实我觉得是一个分类题,对于一个半素数,哪些数的lcmlcmlcm为半素数呢。
首先两个质数的lcmlcmlcm一定为半素数,此时lcm(a,b)=a∗blcm(a,b) = a*blcm(a,b)=a∗b,但注意两个数不能相同,质数不可能是半素数
然后如果a为半素数,那么找到一个b,使得lcm(a,b)=alcm(a,b) = alcm(a,b)=a,也为半素数,这里的b可以等于a,也可以为a的两个因子中的一个
按照上面说的,要找的数只和素数和半素数有关,所以数列中其他的数可以直接扔掉了。
由于数据范围中aia_iai的值不大,所以可以直接先预处理出2∗1052*10^52∗105以内的素数和半素数,记录下来给出的数列中哪些数是素数哪些是半素数,再去看能够组合出多少对即可。还是因为素数和半素数不可能相同,所以不用考虑重复的问题。
1、找两个不相等的质数的组合数,例如用a1,a2,..,axa_1,a_2,..,a_xa1,a2,..,ax表示数列中不同质数的数量,其和为sumasum_asuma,之后求∑i=1nai(sum−prei)\sum_{i = 1}^{n}a_i(sum-pre_i)∑i=1nai(sum−prei),preipre_iprei表示a的前缀
2、找两个相等的半素数,注意可以是下标相同的数,所以对于有x个半素数a,对答案的贡献为(x+1)∗x/2(x+1)*x/2(x+1)∗x/2
3、找一个半素数和其因数的组合,可以在预处理的时候顺便将每个半素数的两个因数也处理出来,再直接加上因数的个数乘对应半素数的个数即可
const int N = 200000;
vector<i64> prime;
map<i64, pair<int,int>> semi;
bool vis[N+10];
int has[N+10];
void euler_sieve(){
for(int i = 2;i<=N;i++){
if(!vis[i]){
prime.pb(i);
has[i] = 1;
}
for(int j = 0;j<prime.size();j++){
if(i*prime[j]>N) break;
vis[i*prime[j]] = 1;
if(i%prime[j] == 0) break;
}
}
}
void semiprime(){
for (int i = 0; i < prime.size(); i++){
for (int j = i; j < prime.size(); j++) {
i64 x = prime[i] * prime[j];
if (x >= N) break;
semi[x] = {prime[i], prime[j]};
}
}
};
void solve() {
int ans = 0;
int n;cin>>n;
vector<int> a(n);
int ma = 0;
for(int i = 0;i<n;i++){
cin>>a[i];
ma = max(ma,a[i]);
}
map<int,int> mp1;
for(int i = 0;i<n;i++){
if(has[a[i]]) mp1[a[i]]++;
}
map<array<int,3>,int> mp2;
for(int i = 0;i<n;i++){
if(semi[a[i]].fi){
if(has[semi[a[i]].fi] || has[semi[a[i]].se]){
mp2[{a[i],semi[a[i]].fi,semi[a[i]].se}]++;
}
}
}
for(auto &[x,y]:mp2){
ans+=mp1[x[1]]*y;
ans+=mp1[x[2]]*y;
if(x[1] == x[2]){
ans-=mp1[x[2]]*y;
}
if(y){
ans+=(1+y)*y/2;
}
}
int sum = 0;
for(auto &[x,y]:mp1){
sum+=y;
}
for(auto &[x,y]:mp1){
sum-=y;
ans+=sum*y;
}
cout<<ans<<endl;
}
signed main(){
cin.tie(0)->ios_base::sync_with_stdio(0);
int t = 1;
cin>>t;
euler_sieve();
semiprime();
while(t--){
solve();
}
return 0;
}
H.Bro Thinks He’s Him
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using u128 = unsigned __int128;
#define endl "\n"
#define pii pair<int,int>
#define pIi pair<i64,int>
#define piI pair<int,i64>
#define PII pair<i64,i64>
#define pb push_back
#define fi first
#define se second
const int mod = 998244353;
const int N = 200005;
i64 fp(i64 a, int n) {
i64 ans = 1;
a %= mod;
while (n) {
if (n & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
struct Seg {
vector<i64> tree;
int size;
Seg(int n) {
size = 1;
while (size < n) size <<= 1;
tree.assign(2 * size, 0);
}
void add(int pos, i64 val) {
pos += size - 1;
tree[pos] = ((tree[pos] + val)+mod)%mod;
for (pos >>= 1; pos >= 1; pos >>= 1) {
tree[pos] = ((tree[pos << 1] + tree[(pos << 1) + 1])+mod)%mod;
}
}
i64 query(int l, int r) {
l += size - 1;
r += size - 1;
i64 res = 0;
while (l <= r) {
if (l % 2 == 1) res = ((res + tree[l++])+mod)%mod;
if (r % 2 == 0) res = ((res + tree[r--])+mod)%mod;
l >>= 1;
r >>= 1;
}
return res;
}
};
void solve(){
string str;
string s;
int m;
cin >> str >> m;
i64 n = str.size();
s = string(n + 1, '0');
i64 res = ((fp(2, n) - 1)+mod)%mod;
Seg sum0(n), sum1(n);
auto work = [&](int idx){
i64 l1,r1,l0,r0,ans,f;
if(idx>1){
l1 = sum0.query(1,idx-1);
}
else{
l1 = 0;
}
if(idx<n){
r1 = sum1.query(idx+1,n);
}
else{
r1 = 0;
}
l0 = ((fp(2, idx-1) - 1 - l1)+mod)%mod;
r0 = ((fp(2, n - idx) - 1 - r1)+mod)%mod;
ans = ((l0 + r0 + 2 * l0 * r0 - l1 - r1 - 2 * l1 * r1)+mod)%mod;
if(s[idx] == '0'){
f = 1;
}
else{
f = -1;
}
res = ((res + f * ans)+mod)%mod;
sum0.add(idx, f * fp(2, idx-1));
sum1.add(idx, f * fp(2, n - idx));
s[idx] = s[idx]^1;
};
for (int i = 0; i < n; ++i) {
if (str[i] == '1') {
work(i + 1);
}
}
while (m--) {
int idx;
cin >> idx;
work(idx);
cout << (res+mod)%mod << ' ';
}
cout<<endl;
}
signed main() {
cin.tie(0)->ios_base::sync_with_stdio(0);
int t = 1;
cin>>t;
while(t--){
solve();
}
return 0;
}
1185

被折叠的 条评论
为什么被折叠?



