D题:Reverse Madness
题意:
给你一串长为 n 的字符串 s,以及k个间隔 l , r 。l与r有以下特点。
l1=1;
rk=n;
li≤ri, for each positive integer i such that 1≤i≤k;
li=ri−1+1, for each positive integer i such that 2≤i≤k;
之后给q个操作,每个操作给一个x,以下是x的特点以及操作解释:
Find an index i such that li≤x≤ri (notice that such i is unique).
Let a=min(x,ri+li−x) and let b=max(x,ri+li−x).
Reverse the substring of s from index a to index b.
题解:
可以用差分的方法消除重复翻转的区间。
有个关键是a到b区间的字符串中间位置是(r [ i ]+l [ i ])/2, 且 li≤x≤ri ,每次翻转行为都被固定在区间内,可以正常枚举题目给的区间进行翻转得到答案。
官方题解:将字符串拆分为k个特定区间,每次翻转都会在区间内且中间值为(r [ i ]+l [ i ])/2,在输入q次操作时将对应cnt【pos】++(记录操作数)。依旧是枚举k个区间,如果两个关于中间位置对称的位置的cnt和为偶数就是完全抵消,奇数就再做一次。但这种做法只能抵消翻转字符完全相同的情况,所以耗时会多一点。
参考:Codeforces Round 900 (Div. 3) A - G - 知乎 (zhihu.com)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
typedef pair<ll,ll>PII;
int l[N],r[N];
int d[N];
void solve(){
int n,k;
cin>>n>>k;
string s;
cin>>s;
s=" "+s;
memset(d,0,sizeof d);
for(int i=0;i<k;i++)cin>>l[i];
for(int i=0;i<k;i++)cin>>r[i];
int q;
cin>>q;
while(q--){
int x;
cin>>x;
int pos=lower_bound(r,r+k,x)-r;//用函数查找对应区间
int mid=(r[pos]+l[pos])/2;
int b=r[pos]+l[pos]-x;
if(b>x)swap(b,x);
d[b]^=1;//确定区间
if(mid+1<=n)d[mid+1]^=1;//注意判断数组有没有越界
}
for(int i=1;i<=n;i++){
d[i]^=d[i-1];
}
for(int i=0;i<k;i++){
int mid=(r[i]+l[i])/2;
for(int k=mid;k>=l[i];k--){
if(d[k])swap(s[k],s[l[i]+r[i]-k]);
}
}
cout<<s.substr(1)<<endl;
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin>>t;
//t=1;
while (t--)
{
solve();
}
return 0;
}
E题:Iva & Pav
题意:给你一串数和q个查询,每次查询都要找到最大的右边界使得al & al+1 &…ar大于等于k
题解:
因为限制时间给得非常宽裕,所以完全可以把每个数转换为二进制,并且进行前缀和计算,也就是计算前i个数中有多少个数的二进制在某一位上为1,如果区间内此位置为1的个数与区间长度相同,必然在与操作后保留为1。
因为与操作会使数越做越小,所以可以刚开始就将第l个数与k比较,如果小于k直接输出-1。在寻找时用二分查找,原因同上,且可以用一个check函数直接获取最终得到的数。
参考:Codeforces Round 900 (Div. 3) A~F_死性不改.的博客-CSDN博客
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define has1 __builtin_popcount
const int N = 2e5 + 5;
ll f[N][32];
ll a[N];
void fun(int id) // 将给定的数化为32位二进制数
{
ll x = a[id];
for (int j = 0; j < 32; j++)
{
f[id][j] = f[id - 1][j];
if (x % 2 == 1)
{
f[id][j]++;
}
x /= 2;
}
}
ll ans, n, q, k;
ll check(ll l, ll r)
{
ll x = 0, y = 1;
for (int i = 0; i < 32; i++)
{
int p = f[r][i] - f[l - 1][i];
if (p == r - l + 1)
{
x += y;
}
y *= 2;
}
return x;
}
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
fun(i);
}
int q;
cin >> q;
while (q--){
ll l,k,mid,r;
r=n;ans=-1;
cin>>l>>k;
if(a[l]<k){cout<<"-1"<<" ";continue;}
ll l0=l;
while(l<=r){
mid=(l+r)/2;
if(check(l0,mid)>=k){
ans=mid;
l=mid+1;
}
else{
r=mid-1;
}
}
cout<<ans<<" ";
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
F题:Vasilije Loves Number Theory
题意; 给你一个数n,做q次操作。第一种操作是给一个x,将n变为n*x,询问是否存在一个a使得a与n互质并且n*a有n个因数。第二种操作是将n变为最开始的n。
题解:
因为gcd(a,n)==1,所以d(n*a)=d(n)*d(a)。要使d(n*a)==n,即n%d(n)==0(就不用考虑a啦!).
d(n)求解方法为:先从1开始筛选n的因子并记录个数,因为这些因子可以自由组合为n的其他因子,所以通过乘他们的个数+1获得总因子个数。
在判断n%d(n)是否为0时可以拆开成因数求余 (a*b)%c==((a%c)*(b%c))%c;
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
#define has1 __builtin_popcount
const int N =505;
map<ll,ll>now,org;//记录质因数
ll qpow(ll a,ll b,ll m){
a%=m;
ll res=1;
while(b>0){
if(b&1)res=res*a%m;
a=a*a%m;
b>>=1;
}
return res%m;
}
void solve(){
now.clear();
ll n,q,x;
cin>>n>>q;
x=n;
for(int i=2;i*i<=x;i++){
while(x%i==0){
x/=i;
now[i]++;
}
}
if(x>1){
now[x]++;
}
org=now;//记录最开始的因子情况
while(q--){
int op;
cin>>op;
if(op==1){
ll s;
cin>>s;
for(int i=2;i*i<=s;i++){
while(s%i==0){
s/=i;
now[i]++;
}
}
if(s>1)now[s]++;
ll ans=1;
for(auto v:now){
ans*=(v.second+1);//得出因子数量
//每个因子可以不选或选<=v.second个
}
ll mod=1;
for(auto v:now){
mod=(mod*qpow(v.first,v.second,ans))%ans;
}
if(mod%ans==0)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
else{
now=org;//恢复成原先的map
}
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
//t=1;
while (t--)
{
solve();
}
return 0;
}