CodeTON Round 9 (Div. 1 + Div. 2, Rated, Prizes!)题解记录(A~D,附带E规律公式)
比赛链接:https://codeforces.com/contest/2039
这次被C2卡了两个小时,D题最后提前一分钟AC,有点刺激。感觉做了些难题,但是rating没加多少,看来得避免和红名和橙名同台竞技。下次div1+div2就不打了,等两周后打打div2?正好得准备期末考试了。
A. Shohag Loves Mod
题面:
Shohag 有一个整数 $ n $。请帮助他找到一个递增的整数序列 $1 \le a_1 < a_2 < \ldots < a_n \le 100 $,使得对于所有的 $1 \le i < j \le n $ 都满足 $a_i \bmod i \neq a_j \bmod j $。
它表明,在给定的约束下,这样的序列总是存在的。
$^{\ast} $ $a \bmod b $ 表示 $a $除以 $b $后的余数。例如, $7 \bmod 3 = 1 $, $8 \bmod 4 = 0 $ 和 $69 \bmod 10 = 9 $。
输入:
第一行包含一个整数 $t $(\(1 \le t \le 50\))——测试用例的数量。
每个测试用例的唯一一行包含一个整数 $n $(\(2 \le n \le 50\))。
输出:
对于每个测试用例,打印一个满足题目中提到的条件的非空字符串,或者如果没有这样的字符串存在,则打印 $-1 $。如果有多个解决方案,请输出任意一个。
样例:
2
3
6
——————
2 7 8
2 3 32 35 69 95
思路:主要是得找到最短符合答案,不然包被hack得。发现第一个数可以为2,于是选了2,然后第2个数可以为3且3%2==1,不等于0,可以!。然后4不可以,5可以,这样浅浅枚举下,2以后,输出3,5,7这种等差数列即可,所以其实可以写成2*i-1的
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<stdio.h>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 5e5+5;
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll ksm(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans%mod*(x%mod)%mod;
x=x%mod*(x%mod)%mod;
y>>=1;
}
return ans%mod%mod;
}
ll gcd(ll x,ll y)
{
if(y==0)
return x;
else
return gcd(y,x%y);
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
if(n>0)
cout<<2<<" ",n--;
for(ll i=3;i<=100;i+=2)
{
if(n==0)
break;
n--;
cout<<i<<" ";
}
cout<<endl;
}
}
B. Shohag Loves Strings
题面:
对于一个字符串 $ p $,让 $ f(p) $表示 $ p $ 的不同非空子串的数量。
Shohag 有一个字符串 $ s $。帮助他找到一个非空字符串 \(p\),使得$ p $ 是 $ s $的一个子串,并且 \(f(p)\) 是偶数,或者声明没有这样的字符串存在。
\(^{\ast}\) 如果可以通过从 $ b $ 中删除一些(可能为零个或全部)字符从开头和一些(可能为零个或全部)字符从末尾来获得 $ a $,则称字符串 $ a $ 是字符串 $ b$ 的一个子串。
输入:
第一行包含一个整数 $ t $(\(1 \le t \le 10^4\))——测试用例的数量。
每个测试用例的唯一一行包含一个字符串 $ s $(\(1 \le |s| \le 10^5\)),由小写英文字母组成。
保证所有测试用例中 $ s $ 的长度之和不超过$3 \cdot 10^5 $。
输出:
对于每个测试用例,打印一个满足题目中提到的条件的非空字符串,或者如果没有这样的字符串存在,则打印 \(-1\)。如果有多个解决方案,请输出任意一个。
样例:
5
dcabaac
a
youknowwho
codeforces
bangladesh
abaa
-1
youknowwho
eforce
bang
思路:其实枚举下,形如abc,aa,abcd都是可以的。所以特判长度为1或2,然后先检测有没相邻两个相同的,有就输出答案,没有就检测有没有f[i]!=f[i+2]且由三个字符构成的串,由就输出答案,没有就只能输出-1.不会有人不会substr吧。
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<stdio.h>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 5e5+5;
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll ksm(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans%mod*(x%mod)%mod;
x=x%mod*(x%mod)%mod;
y>>=1;
}
return ans%mod%mod;
}
ll gcd(ll x,ll y)
{
if(y==0)
return x;
else
return gcd(y,x%y);
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
string f;
cin>>f;
if(f.size()==1)
cout<<-1<<endl;
else if(f.size()==2)
{
if(f[0]==f[1])
cout<<f<<endl;
else
cout<<-1<<endl;
}
else
{
ll pd=0;
string c;
for(ll i=0;i<f.size()-1;i++)
{
if(f[i]==f[i+1]&&pd==0)
{
c=f.substr(i,2);
pd=1;
}
}
for(ll i=0;i<f.size()-1;i++)
{
if(i+2<=f.size()-1&&pd==0&&f[i]!=f[i+2])
{
c=f.substr(i,3);
pd=1;
}
}
if(pd)
cout<<c<<endl;
else cout<<-1<<endl;
}
}
}
C1. Shohag Loves XOR (Easy Version)
题面:
这是问题的简单版本。两个版本之间的差异用粗体标出。只有在解决了问题的两个版本后,你才能进行hack。
Shohag有两个整数 $ x $ 和 $ m $。帮助他计算满足 $1 \le y \le m $且 $ \mathbf{x \neq y} $并且 \(x\) \(\oplus\) \(y\)是一个因数\(^{\text{∗}}\) 的 \(x\)、\(y\) 或两者的整数数量。这里 \(\oplus\) 是位异或运算符。
\(^{\text{∗}}\) 如果存在一个整数 $ c $ 使得 $a = b \cdot c $,则称数字 $ b $ 是数字 $ a $ 的一个因数。
输入:
第一行包含一个整数 $ t $(\(1 \le t \le 10^4\))——测试用例的数量。
每个测试用例的唯一一行包含两个用空格分隔的整数 \(x\)和 $ m$(\(1 \le x \le 10^6\),\(1 \le m \le 10^{18}\))。
保证所有测试用例中 $ x $ 的总和不超过 $10^7 $。
输出:
对于每个测试用例,打印一个整数——合适的 $ y $ 的数量。
样例:
5
6 9
5 7
2 3
6 4
4 1
3
2
1
1
0
思路:这题真的不难,送分的。显然\(x\) \(\oplus\) \(y\)得是x或y的因数,那首先肯定要比x或着y小,而知道如果两个数的最大二进制位相同,则两者一定不会出现一个数是另一个因数的情况。这样就可以很好解决了,我就只考虑到比x大一点就好了,所以直接求出比x的二进制大一位的位置的数字然后减1,再和m取个min枚举就好了,这样直接枚举不超过x的2倍,而x总数小于等于1e7,枚举不会TLE,记得特判同位置不可算就好了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<stdio.h>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 5e5+5;
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll ksm(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans%mod*(x%mod)%mod;
x=x%mod*(x%mod)%mod;
y>>=1;
}
return ans%mod%mod;
}
ll gcd(ll x,ll y)
{
if(y==0)
return x;
else
return gcd(y,x%y);
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll x,m;
cin>>x>>m;
ll pd=0;
for(ll i=0;i<=60;i++)
{
if(x&(1ll<<i))
{
pd=i;
}
}
ll u=min(m,(1ll<<(pd+1))-1);
ll ans=0;
for(ll i=1;i<=u;i++)
{
ll j=(x^i);
if(j==0)continue;
if(x%j==0||i%j==0)
ans++;
}
cout<<ans<<endl;
}
}
C2. Shohag Loves XOR (Hard Version)
题面:
这是问题的困难版本。两个版本之间的差异用粗体标出。只有在解决了问题的两个版本后,你才能进行hack。
Shohag有两个整数 \(x\)和 \(m\)。帮助他计算满足 $ 1 \le y \le m$的整数数量,使得 \(x\) \(\oplus\) \(y\) 是能被整除\(^{\text{∗}}\)的 \(x\)、\(y\) 或两者。这里 \(\oplus\) 是位异或运算符。
\(^{\text{∗}}\)如果存在一个整数 $c $使得 $ a = b \cdot c $,则称数字 \(a\) 能被数字 \(b\) 整除。
输入:
第一行包含一个整数 $ t $(\(1 \le t \le 10^4\))——测试用例的数量。
每个测试用例的唯一一行包含两个用空格分隔的整数 \(x\)和\(m\)(\(1 \le x \le 10^6\),\(1 \le m \le 10^{18}\))。
保证所有测试用例中 $ x $ 的总和不超过 $ 10^7 $。
输出:
对于每个测试用例,打印一个整数——合适的 $ y $ 的数量。
样例:
5
7 10
2 3
6 4
1 6
4 1
————————
3
2
2
6
1
思路:真的好吃😋,这道题卡了我快2个小时!!!!做不出来不要死磕着啊
好在第二小时左右过了,这里主要是得想办法把\(x\) \(\oplus\) \(y\)区间化,首先x及以下的我可以暴力先求出来,这里先把x的涉及的二进制区先枚举完,然后得再枚举一个二进制大一点得2的次幂减1,这样子就可以保证这个数是区间左端(这里得看有没有超过m,超过了就直接枚举到m输出即可),然后现在考虑区间右端,发现如果\(x\) \(\oplus\) \(m\)后最大值不能确定啊?所以这时候得直接对m进行二进制扫描,如果对应二进制存在且大于等于x就继续扫,直到扫不动,这里扫到得2的次幂得加起来,然后暴力枚举m到这个数(这里得判断有没有小于区间左端),随后扫到的数-1就为右端点,直接再加右端点除以x-左端点除以x即可得到答案。
这里证明下正确性,显然对于二进制1011111,其异或二进制1011,如果枚举0~1011111所对应的数,一定会把区间所有可能的数异或出来,且最大值一定为1011111所对应的数。
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<stdio.h>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 5e5+5;
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll ksm(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans%mod*(x%mod)%mod;
x=x%mod*(x%mod)%mod;
y>>=1;
}
return ans%mod%mod;
}
ll gcd(ll x,ll y)
{
if(y==0)
return x;
else
return gcd(y,x%y);
}
ll ck(ll x,ll m)
{
ll cnt=0;
for(ll i=1;i<=m;i++)
{
ll u=(i^x);
if(u%i==0||u%x==0)
cnt++;
}
return cnt;
}
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll x,m;
cin>>x>>m;
if(x==1)
{
cout<<m<<endl;
continue;
}
ll pd=0;
for(ll i=0;i<=60;i++)
{
if(x&(1ll<<i))
{
pd=i;
}
}
ll u=min(m,(1ll<<(pd+1))-1);
ll ans=0;
ll op=0;
ll jo=0;
for(ll i=1;i<=u;i++)
{
ll k=(x^i);
if(k%x==0||k%i==0)
ans++;
}
if((1ll<<pd+2)-1>=m)
{
for(ll k=(1ll<<pd+1);k<=m;k++)
{
ll f=(x^k);
if(f%x==0||f%k==0)
ans++;
}
cout<<ans<<endl;
continue;
}
else
{
for(ll k=(1ll<<pd+2)-1;k>=(1ll<<pd+1);k--)
{
ll f=(x^k);
if(f%x==0||f%k==0)
ans++;
}
op=(1ll<<pd+2)-1;
}
if(u!=m)
{
ll cs=op/x;
ll cnt=0;
ll ko=0;
ll pd=0;
ll h=0;
for(ll i=60;i>=0;i--)
{
if(m&(1ll<<i)&&cnt==0)
{
cnt=1;
ko+=(1ll<<i);
}
else if(m&(1ll<<i)&&cnt==1)
{
if((1ll<<i)>=x)
ko+=(1ll<<i),h=i;
}
}
ll d=max(ko,op+1);
for(ll j=m;j>=d;j--)
{
ll u=(j^x);
if(u%x==0||u%j==0)
ans++;
}
if(d!=op+1)
{
ko-=(1ll<<h);
ko+=(1ll<<h)-1;
ans+=ko/x-op/x;
}
cout<<ans<<endl;
//cout<<ck(x,m)<<endl;
}
}
}
D. Shohag Loves GCD
题面:
Shohag 有一个整数 \(n\) 和一个包含 \(m\) 个唯一整数的集合 \(S\)。帮助他找到一个字典序最大的整数数组 \(a_1, a_2, \ldots, a_n\),使得对于每个 \(1 \le i \le n\),都有 \(a_i \in S\),并且对于所有 \(1 \le i < j \le n\),都满足 \(a_{\gcd(i, j)} \neq \gcd(a_i, a_j)\),或者声明不存在这样的数组。
\(^{\ast}\) 如果数组 \(a\) 和相同长度的数组 \(b\) 不相等,并且在 \(a\) 和 \(b\) 不同的第一个位置,数组 \(a\) 的元素比 \(b\) 中对应的元素大,则称数组 \(a\) 字典序大于数组 \(b\)。
\(^{\dagger}\) \(\gcd(x, y)\) 表示整数 \(x\) 和 \(y\) 的最大公约数 (GCD)。
输入:
第一行包含一个整数 \(t\)(\(1 \le t \le 10^4\))——测试用例的数量。
每个测试用例的第一行包含两个整数 \(n\) 和 \(m\)(\(1 \le m \le n \le 10^5\))。
每个测试用例的第二行包含 \(m\) 个唯一整数,按递增顺序排列,代表集合 \(S\) 的元素(对于每个 \(x \in S\),有 \(1 \le x \le n\))。
保证所有测试用例中 \(n\) 的总和不超过 \(3 \cdot 10^5\)。
输出:
对于每个测试用例,如果没有解决方案,请打印 \(-1\),否则打印 \(n\) 个整数——满足条件的字典序最大的整数数组。
样例:
3
6 3
3 4 6
1 1
1
2 1
2
————————
6 4 4 3 4 3
1
-1
思路:虽然没C2好吃,但是也是快结束才交,早在2.30就交了发,WA6了,想了会才发现错误点.要字典序最大,所以第一个肯定得放最大的数,由题目 \(a_{\gcd(i, j)} \neq \gcd(a_i, a_j)\),可得,对于1以外的数都不能等于这个数了,所以第一个数固定。对于第二个数,得放得尽可能多的位置,所以对于每个为质数的位置,我都放第二大的数。好了现在考虑第三大的数,选择目前能选的最小位置的数,然后暴力计标记一遍其所有倍数,然后继续遍历,遍历到没标记的重复之前的操作。随后再用vector解标记,删位置,后面都重复此类似操作就行了。我之所以WA6,是因为第三次及以后只考虑了第一个数的倍数,后面都没考虑,想了下此时,18,30就是我的反例,因为他们不满足\(a_{\gcd(i, j)} \neq \gcd(a_i, a_j)\)。
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<stdio.h>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 5e5+5;
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll ksm(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans%mod*(x%mod)%mod;
x=x%mod*(x%mod)%mod;
y>>=1;
}
return ans%mod%mod;
}
ll gcd(ll x,ll y)
{
if(y==0)
return x;
else
return gcd(y,x%y);
}
ll ck(ll x,ll m)
{
ll cnt=0;
for(ll i=1;i<=m;i++)
{
ll u=(i^x);
if(u%i==0||u%x==0)
cnt++;
}
return cnt;
}
ll a[150000];
ll b[150000];
bool st[150000];
ll d[150000];
bool c[150000];
ll gs=0;
void ola(ll x)
{
for(ll i=2;i<=x;i++)
{
if(st[i]==0)gs++,d[gs]=i,c[i]=1;
for(ll j=1;d[j]<=x/i;j++)
{
st[i*d[j]]=1;
if(i%d[j]==0)
break;
}
}
}
ll vis[150000];
set<ll>g;
vector<ll>ok;
vector<ll>fo;
int main()
{
fio();
ll t;
cin>>t;
ola(100000);
while(t--)
{
g.clear();
ok.clear();
fo.clear();
ll n,m;
cin>>n>>m;
for(ll i=1;i<=m;i++)
cin>>a[i];
ll u=m;
for(ll i=1;i<=n;i++)
{
vis[i]=0;
g.insert(i);
}
for(ll k=m;k>=1;k--)
{
if(k==m)
{
ll d=0;
for(auto j:g)
{
b[j]=a[m];
d=j;
break;
}
g.erase(d);
}
else if(k==m-1)
{
ok.clear();
for(auto j:g)
{
if(c[j])
{
b[j]=a[k];
ok.push_back(j);
}
}
for(auto j:ok)
{
g.erase(j);
}
}
else
{
ok.clear();
fo.clear();
ll u=0;
for(auto j:g)
{
if(vis[j])continue;
if(u==0)
u=j,b[j]=a[k],ok.push_back(j);
else
{
u=j;
b[j]=a[k];
ok.push_back(j);
}
for(ll z=1;z*j<=n;z++)
{
if(vis[z*j])continue;
vis[z*j]=1;
fo.push_back(z*j);
}
}
for(auto j:fo)vis[j]=0;
for(auto j:ok)
{
g.erase(j);
}
}
}
if(g.size()>0)
cout<<-1<<endl;
else
{
for(ll j=1;j<=n;j++)
cout<<b[j]<<" ";
cout<<endl;
}
}
}
E. Shohag Loves Inversions
题面:
Shohag 有一个整数数组 a
。最初 a = [0, 1]
。他可以重复执行以下操作任意次数:
- 设
k
为当前数组a
中的反转数∗。 - 在
a
中的任何位置插入k
,包括开头或结尾。
例如,如果 a = [4, 6, 2, 4]
,则反转数为 k = 3
。所以 Shohag 在操作后可以得到以下数组:[3, 4, 6, 2, 4]
、[4, 3, 6, 2, 4]
、[4, 6, 3, 2, 4]
、[4, 6, 2, 3, 4]
和 [4, 6, 2, 4, 3]
。
给定一个整数 n
,帮助 Shohag 计算,模数 998244353
,执行操作后可以获得的不同长度 n
数组的数量。
∗ 数组 a
中反转数是索引对 (i, j)
的个数,使得 i < j
且 \(a_i\) > \(a_j\)。
输入:
- 第一行包含一个整数 \(t\)(\(1 \leq t \leq 10^4\))——测试用例的数量。
- 每个测试用例的第一行也是唯一一行包含一个整数 \(n\)(\(2 \leq n \leq 10^6\))。
保证所有测试用例的 \(n\) 之和不超过 \(10^6\)。
输出:
对于每个测试用例,输出一个整数 — 可能的数组数模 \(998244353\)。
样例:
4
4
2
7
69
——————
5
1
682
325188814
公式版:
思路:本题正解为dp,这里给出评论区看到的大佬推的公式(orz,请看代码),dp日后补充
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll p=rnd()%mod;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[1000002];
int main()
{
fio();
a[2]=1,a[3]=2,a[4]=5,a[5]=19;
for(ll i=2;i<=1000000-2;i++)
{
a[i+2]=((a[i+1]%mod*(i+3)%mod)%mod-(a[i]%mod*((i+2)%mod))%mod-1)%mod;
a[i+2]+=1000*mod;
a[i+2]%=mod;
}
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
cout<<a[n]<<endl;
}
}