Codeforces Round 889 (Div. 2)

文章详细解析了CodeforcesRound889(Div.2)中的三道编程题目,包括A题DaltontheTeacher的签到题解法,B题LongestDivisorsInterval的整除性思考,以及C题Dual的两种难度版本的构造策略。解答涉及动态规划和数组操作,适合程序员和竞赛爱好者学习。
摘要由CSDN通过智能技术生成

Codeforces Round 889 (Div. 2)

A. Dalton the Teacher

思路:签到题
记录当前值和下标相等的个数res,然后输出(res+1)/2即可

#include <iostream>
#include <cstring>
#include <algorithm>
#include<tuple>
#include <vector>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <cmath>
#include<functional>
using namespace std;
const int N =2e6+7,mod=998244353;
#define rep(i,a,n) for(int i=a;i<=n;i++)
typedef pair<int, int> PII;
typedef long long LL;
int n,m,k;
int a[N];
void solve(){
    cin>>n;
    rep(i,1,n)cin>>a[i];
    long long res=0;
    rep(i,1,n)if(i==a[i])res++;
    cout<<(res+1)/2<<endl;;
}
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    while(t--) solve();
    return 0;
}

B. Longest Divisors Interval

思路:连续2个数字里边必定有能整除2的,连续3个数字里边必定有能整除3的,…….连续n个数字里边必定有能整除n的,所以只需要从1开始判断直到不能被整除为止,这个数字好像最大才四十几

#include<bits/stdc++.h>
#define int long long
using namespace std;


signed main(){
	
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;
		if(n%2){
			cout<<1<<'\n';
			continue;
		}
		int ans=1,now=2;
		for(int i=2;i<=n;i++){
			if(n%i==0){
				ans++;
				//n/=i;
			}else break;
			
		}
		cout<<ans<<'\n';
		
	}
	return 0;
}

C1. Dual (Easy Version)

思路:构造题
我们可以找到数组里面绝对值最大的值进行跟新其他点的值,并将最大值进行跟新,如果最大绝对值是正数,正向遍历即可。如果最大绝对值是负数,反向遍历即可

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <cmath>
using namespace std;
const int N =2e5+7,mod=998244353;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define dep(i,a,n) for(int i=n;i>=a;i--)
typedef pair<int, int> PII;
typedef long long LL;
int n,m,k;
int a[N],I[N],J[N];
void solve(){
    cin>>n;
    k=0;
    int mx=-2000,id1;
    rep(i,1,n){
        cin>>a[i];
        if(mx<abs(a[i])){
            mx=abs(a[i]);
            id1=i;
        }
    }
    int k=0;
    if(a[id1]<0){
        mx=a[id1];
        dep(i,1,n-1){
            while(a[i]>a[i+1]){
                a[i]+=mx;
                I[k]=i;
                J[k]=id1;
                k++;
                if(a[i]<mx){
                    mx=a[i];
                    id1=i;
                }
                
            }
        }
        cout<<k<<endl;
        rep(i,0,k-1)cout<<I[i]<<" "<<J[i]<<endl;
        return ;
    }
    for(int i=1;i<n;i++){
        while(a[i]>a[i+1]){
            I[k]=i+1;
            J[k]=id1;
            k++;
            a[i+1]+=mx;
            if(a[i+1]>mx){
                mx=a[i+1];
                id1=i+1;
            }
        }
    }
    cout<<k<<endl;
    rep(i,0,k-1)cout<<I[i]<<" "<<J[i]<<endl;
    
}
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    while(t--) solve();
    return 0;
}

C2. Dual (Hard Version)

思路:这道题比前面一道限制能操作次数更低了,这题我分了三种情况
① 当数组里面全为正数:我们直接正向遍历一遍,把前面加到后面即可
② 当数组里面全为负数:我们直接反向遍历一遍,把后面的加到前面即可
③ 当数组里面既有负数又正数:
(1)我们尝试用绝对值最大的负数更新最大值,然后将数组全变为负数,然后可以在用第二种情况
(2)我们尝试用绝对值最大的正数更新最大值,然后将数组全变为正数,然后可以在用第一种情况

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
const int N=2e6+10,M=2e5;
typedef double db;
typedef pair<int,int>pii;
int n,m,k,Q,cnt,mx,mn;
vector<int>del;
int a[200010],b[200010],x[200010],y[200010];
int prime[N];
bool st[N];
void solve(){
    cin>>n;
    mx=mn=1;
    rep(i,1,n){
        cin>>a[i];
        if(a[i]>a[mx])mx=i;
        if(a[i]<a[mn])mn=i;
    }
    int tmp=a[mn],tot=0;
    //全为非负数
    if(a[mn]>=0){
        cout<<n-1<<endl;
        rep(i,1,n-1)cout<<i+1<<" "<<i<<endl;
        return ;
    }
    //全为非正数
    if(a[mx]<=0){
        cout<<n-1<<endl;
        per(i,2,n)cout<<i-1<<" "<<i<<endl;
        return ;
    }
    //用绝对值最大的负数更新最大值
    while(-a[mn]<a[mx])a[mn]*=2,x[++tot]=mn,y[tot]=mn;
    rep(i,1,n)if(a[i]>0)x[++tot]=i,y[tot]=mn;
    //判断负数更新是否可行
    if(tot+n-1<=31){
        cout<<tot+n-1<<endl;
        rep(i,1,tot)cout<<x[i]<<" "<<y[i]<<endl;
        per(i,2,n)cout<<i-1<<" "<<i<<endl;
        return ;
    }
    else{
        a[mn]=tmp;tot=0;
        while(-a[mn]>a[mx])a[mx]*=2,x[++tot]=mx,y[tot]=mx;
        rep(i,1,n)if(a[i]<0)x[++tot]=i,y[tot]=mx;
        cout<<tot+n-1<<endl;
        rep(i,1,tot)cout<<x[i]<<" "<<y[i]<<endl;
        rep(i,1,n-1)cout<<i+1<<" "<<i<<endl;
    }
}
signed main(){
    int t;cin>>t;
    while(t--)solve();
}

D. Earn or Unlock

思路:注意卡牌若用于激活后面的牌就不能计数了。

考虑朴素 dp:dpi,j 表示操作到第 i 张牌,已经解锁了后面 j 张牌的状态是否存在。则朴素转移的复杂度是 O(n2),而又因为状态的值只有 0,1 两种情况,容易联想到 bitset 进行优化,滚掉第一维(感觉确实不太会用bitset)

(学习bitset卡常小技巧bushi)

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 1e5 + 5;
ll a[N], s[N], n, ans;
bitset<N*2> f; //f[i][j]: 操作到i,已经解锁了后面j张牌,滚掉第一维

int main () {
    cin >> n;
    for (int i = 1; i <= n; i++)    cin >> a[i], s[i] = s[i-1] + a[i];
    
    f[a[1]] = 1, f[0] = 0, ans = a[1]; //至少拥有第一张牌
    for (int i = 2; i <= n; i++) {
        if (f[i-1])     ans = max (ans, s[i] - (i - 1)); //把值为i-1的那张用来unlock的牌拿走了
        f |= f << a[i];
        f[i-1] = 0; //操作之后把上一张牌拿走了
        //cout << ans << ' ';
    }
    //cout << ans << ' ';
    //for (int i = 0; i < 10; i++)    cout << f[i];
    for (int i = n; i < n * 2; i++) { //从n开始
        if (f[i])   ans = max (ans, s[n] - i);
    }
    cout << ans;
}

//把bitset看成一个字符串

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linkk_bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值