iwtgm-10

题目链接

A.

手玩,左右循环后对应位置字符相同,可得到:
如果只有两个字符一定可以
如果是奇数,那么必须全部相同
如果是偶数,那么奇数位置的要全部相同,偶数位置的要全部相同

卡的点是相对位置不变,可以删除任意位置,如何判奇数全部相同,偶数全部相同
后来看@zys111代码,因为只有两种字符(可相同),可以用一个变量记录下一个需要的字符判

归纳,两个字符一定可以因为奇数只有一个,偶数只有一个,那么一定满足奇数位置和偶数位置分别相同
如果字符全部相同,那么也满足奇数位置和偶数位置分别相同,并且任意个数都可

void solve(){
    string s;cin>>s;
    int ans=inf,cnt,res;
    for(int i=0;i<=9;i++){
        for(int j=0;j<=9;j++){
            res=i;cnt=0;
            for(int k=0;k<s.size();k++){
                if(s[k]-'0'==res){
                    if(res==i)res=j;
                    else res=i;
                }else cnt++;
            }
            if(res==i)ans=min(ans,cnt);//两种不同字符的情况,两种字符数必须相同,此时变量为第一种字符的状态
        }
    }
    cout<<ans<<endl;
}

B.

求前n个数范围内有几个与i互质的和
欧拉函数可算对于i来说,在小于等于i的范围内有几个与i互质
欧拉筛法+欧拉函数可快速算出前n个数的欧拉函数的和
如果m<(n-1)或者m>这个和都不合法(必须要联通且这个和已经是最大的边数)直接退出
如果合法,m只有1e5,在n范围内暴力找与i互质的数输出即可

bool is_prime[N];//是否是质数,0为是,1为不是
int prime[N];//质数数组
int top=1;//质数的下标
int ola[N];//i的欧拉函数值
ll sum[N];//求1-n的欧拉函数的和
int gcd(int a, int b) {
    return b > 0 ? gcd(b, a % b) : a;
}
void get_prime(){
    ola[1]=1;
    for(int i=2;i<N;i++){
        if(!is_prime[i]){//是质数
            prime[top]=i;//存质数
            top++;//下标后移
            ola[i]=i-1;//质数的欧拉函数是质数-1
        }
        for(int j=1;j<top;j++){//最小到达遍历质数数组
            if(i*prime[j]>N)break;
            is_prime[i*prime[j]]=1;//标记质数的倍数即合数
            if(i%prime[j]==0){
                ola[i*prime[j]]=ola[i]*prime[j];//如果i%p==0,那么φ(i*p)=φ(i)*p
                break;//若i是之前质数的倍数,说明这个倍数会在后面的循环内被筛去,无需继续循环
            }
            ola[i* prime[j]]=ola[i]*(prime[j]-1);//如果i%p!=0,那么 φ(i*p)=φ(i)*(p-1)   其中p为质数
        }
    }
}
void solve(){
    get_prime();
    for(int i=1;i<N;i++){
        sum[i]+=sum[i-1]+1ll*ola[i];
    }
    int n,m;cin>>n>>m;
    if(n==1){
        cout<<"Impossible";return ;
    }
    if(m<(n-1)||m>sum[n]-1){
        cout<<"Impossible";return ;
    }
    //cout<<sum[n];
    cout<<"Possible"<<endl;
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            if(gcd(i,j)==1){
                cout<<i<<' '<<j<<endl;
                cnt++;
                if(cnt==m)return ;
            }
        }
    }
}

C.

若第一不要求一定要从0开始,
那么就是贪心,预处理出每个转移器的花费(a[i]+min(i,n-i+1)),然后排序从小到大取就好
因为每次用完转移器要么被转移到0要么转移到n+1,且我们用一个转移器的步数是固定的,转移到离下一个花费最少的转移器更近的点(0或n+1)即可
但现在要求第一步一定要从0开始,考虑如何消除这个影响
先按之前贪心的做法得到一个排序好的数组,对这个数组求一个前缀和,此时下标的值就是能取几个转移器,pre[i]就是总花费
可以枚举第一步选的是哪一个转移器,设下标为id,用花费-差价(没有实际减,只是用这个值去二分)(贪心最优和从0到这个转移器),
此时二分看前缀和数组下标能取到几,设为p,如果p>id,说明此时花费已经包含了我枚举的第一个转移器(这里是贪心取),但差价已经计算过了,下标-1就是答案(因为upper_bound()取的是大于花费的)
如果p<=id,说明没有算第一步的转移器,那么把花费-枚举的那个转移器的花费后再去二分
得到的下标p不用-1因为多的那个1与枚举的那个1抵消

ll n,c,pre[N],ans,cnt;
pair<ll,ll>pa[N];
void solve(){
    ans=0;cin>>n>>c;
    for(ll i=1,x;i<=n;i++){
        cin>>x;
        pa[i]={x+min(i,n-i+1),x+i};
    }
    sort(pa+1,pa+1+n);
    for(int i=1;i<=n;i++)pre[i]=pre[i-1]+pa[i].first;
    for(int i=1;i<=n;i++){
        if(c<pa[i].second)continue;
        cnt= upper_bound(pre+1,pre+1+n,c-pa[i].second+pa[i].first)-pre-1;//upper_bound()给出的下标是大于花费的,所以-1
        if(cnt<i)cnt= upper_bound(pre+1,pre+1+n,c-pa[i].second)-pre;//大于花费的那个1与枚举的那个1抵消,不用-1
        ans=max(ans,cnt);
    }
    cout<<ans<<endl;
}
posted @ 2023-11-02 13:24  WW爆米花  阅读(8)  评论(0编辑  收藏  举报