Codeforces round 900 (Div 3)D~F

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值