【菜鸡补题】2024昆明邀请赛

传送门
个人vp补题初体验,若有不当之处多多指正(抱拳)

A 贪心

  • 注意可能有星级一样的比赛。星级一样,使用之前星级不一样留下的状态
  • 放置的数不超过k
  • 最后输出要保持原来顺序:思路一中把要排序的星级数和属性数据用下标分离开
  • 二维数组开4e5过大 需要使用动态数组

    vector没有分配内存直接下标赋值会卡住 需要push_back

思路

  • 要排序的和不变的分离开
  • 更新之前的上限值
  • 对星级相同的处理:一直使用上一个星级的上限
void solve(){
    int n,m,k;
    cin>>n>>m>>k;
    vector<int>s(n+5),q;
    vector< vector<int> >p(n+2,vector<int>(m+5));
    forr(i,0,n-1){
        q.push_back(i);//q记录下标
        cin>>s[i];
        forr(j,0,m-1)cin>>p[i][j];
    }
    //星级排序 下标移动 属性数据顺序不变 
    sort(q.begin(),q.end(),[&](int x,int y){return s[x]<s[y];});//这里排q使用s的数据 从最小向上递增
    int pres=-1,pre=-1,maxn=pre;//pre是上一个的上限 maxn是星级小于本次的总上限 ※需要一直维护上限maxn
    for(auto i:q){
        // cout<<s[i]<<' ';
        //if(s[i]==pres)tot=pre;//错误 因为前一个与其s相等的 pre可能很大 s相等时没有对p的要求
        if(s[i]!=pres)pre=maxn;//如果s大于上一个s 就更新上限
        int tot=pre+1;//大于之前的上限 tot是至少达到的值
        //直接遍历整行
        int cur=0;
        for(auto &j:p[i]){
            if(j!=-1)cur+=j;//遍历加有值的地方
        }
        //放值
        for(auto &j:p[i]){
            if(j==-1){
                j=(cur>=tot?0:min(k,tot-cur));//注意:放的值[0,k] 如果cur>=tot就是放完了,没值可放
                cur+=j;//每次加上放进去的值 0/k/tot-cur
            }
        }
        // cout<<cur<<','<<tot<<endl;
        // for(auto j:p[i])cout<<j<<' ';cout<<endl;
        if(cur<tot){
            cout<<"No"<<endl;//都放k 也达不到tot的要求
            return ;
        }
        maxn=max(maxn,cur);//记录小的部分最大值
        pres=s[i];
    }
    cout<<"Yes"<<endl;
    forr(i,0,n-1){
        forr(j,0,m-1)cout<<p[i][j]<<' ';
        cout<<endl;
    }
}

I

思路:字符串s中 一段连续字符串长度l (滑动窗口求长度)

  • l为奇数 l-1次操作
  • l为偶数 l次操作

移动时l偶数变成奇数优化,能且只能优化掉一次操作

void solve(){
    string s;
    cin>>s;
    int n=s.size();
    if(n==1)return cout<<0<<endl,void();
    vector<int>l;
    int i=0;
    for(int j=1;j<=n;j++){
        while (s[i]==s[j]&&j<n)j++;
        l.push_back(j-i);
        i=j;
    }
    if(i<n)l.push_back(n-i);
    if(l.size()==1)return cout<<l[0]/2<<endl,void();
    if(s.front()==s.back()){
        l.front()+=l.back();
        l.pop_back();
    }
    int opti=0,ans=0;
    for(auto &i:l){
        ans+=i>>1;
        if(i%2==0)opti=1;
    }
    cout<<ans-opti<<endl;
}

B

人生当中第一个在团队vp中wa的题

  • 错解
    • 第一次wa
void solve(){
    int n,k,ans=0;cin>>n>>k;//竞赛的数量 金牌的比例
    int a[N]={0};
    forr(i,1,n){
        cin>>a[i];
    }
    int m;
    cin>>m;//要分配的队伍数
    int tmp=m;
    forr(i,1,n){ans+=(a[i]/k);a[i]=k-a[i]%k;}//ans记录分配前的金牌数量 a[i]是增加所需
    sort(a+1,a+1+n,cmp);
    //forr(i,1,n)cout<<a[i]<<' ';
    ans+=m/k;//m还能增加的数量  思路错误
    tmp%=k;//m中增加完后剩下的
    forr(i,1,n){
        if(a[i]<=tmp){
            tmp-=a[i];
            ans++;
        }
    }
    cout<<ans<<endl;
}
/*应该先满足好满足的
wa例子
3 10
12 11 10
17
应该是5 但是输出了4 wa
  • 第二次tle
void solve(){
    int n,k,ans=0;cin>>n>>k;
    int a[N]={0};
    forr(i,1,n){
        cin>>a[i];
    }
    int m;
    cin>>m;
    int tmp=m;
    forr(i,1,n){ans+=(a[i]/k);a[i]=k-a[i]%k;}
    sort(a+1,a+1+n,cmp);
    //forr(i,1,n)cout<<a[i]<<' ';
    int idx=1;
    while(tmp){
        if(idx>n){idx=1;}//idx多次重复 会tle
        if(tmp>=a[idx]){
            tmp-=a[idx];
            a[idx++]=k;
            ans++;
        }
        if(idx>n){idx=1;}
        if(tmp<a[idx])break;
    }
    cout<<ans<<endl;
}
  • 正解
void solve()
{
	int n,k;
	cin>>n>>k;
	LL ans=0;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		ans+=(a[i]/k);
		a[i]=(k-a[i]%k);
	}
	int m;cin>>m;
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(m==0) break;
		if(m>=a[i])
		{
			ans++;
			m-=a[i];
		}
		else
		{
			m=0;
			break;
		}
	}//先往每个位置上放
	if(m) ans+=(m/k);
	cout<<ans<<endl;
}

G 位操作

异或:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
1^2 = 3
2^3=1
1^3=2
012^3=0 456^7=0 …
n=1 和 n每4的倍数 连续异或=0 不满足
102 1023(imp) 10243 每到4调换顺序(用queue)

void solve(){
    int n;
    cin>>n;//0~n-1
    if(n==1||n%4==0)return cout<<"impossible"<<endl,void();
    queue<int>tmp;
    forr(i,0,n-1){
        if(i==0||(i+1)%4==0){
            tmp.push(i);
        }else{
            cout<<i<<' ';
             if(!tmp.empty()){
                cout<<tmp.front()<<' ';tmp.pop();
            }
        }
    }
    cout<<endl;
    return ;
}

E gcd

  • 找最大公因数
    辗转相除
int gcd(int a,int b){
	return b>0?gcd(b,a%b):a;
}

gcd(a,b)<a也<b
gcd(b,a%b)<a%b
※a%b<a/2;
所以对一个数N最多有logN个不同公约数

  • 求一段区间共同的gcd->判断前面的gcd是不是本次数的gcd
  • 思路:找lr区间 1~l l~r r~n
    1~l:前缀gcd不为1
    r~n:后缀gcd不为1
    l~r:+k 确定gcd max
  • 直接枚举lr肯定超时,需要优化
    • l是前缀gcd即将要变小的地方
    • r从l向后枚举,对lr之间a[i]+k,放进当前前缀gcd(就是g),然后和后缀gcd求,记录最大
void solve(){
    int n,k;
    cin>>n>>k;
    vector<int>a(n+5);
    forr(i,1,n){
        cin>>a[i];
    }
    vector<int>pre(n+5);//前缀
    vector<int>sub(n+5);//后缀
    pre[1]=a[1],sub[n]=a[n];
    forr(i,2,n)pre[i]=__gcd(a[i],pre[i-1]);
    //forr(i,1,n)cout<<pre[i]<<' ';cout<<endl;
    for(int i=n-1;i>=1;i--)sub[i]=__gcd(a[i],sub[i+1]);
    
    int ans=pre[n];//目前最大公因数
    forr(i,1,n){
        int l,g;
        if(pre[i]!=pre[i-1]){//i=1时 pre[0]=0!=pre[1] 会在最开始时遍历一次
            l=i,g=pre[i-1];//前缀gcd可能不是一直减小?
            forr(r,l,n){//对每个l 枚举r的范围
                g=__gcd(g,a[r]+k);
                ans=max(__gcd(g,sub[r+1]),ans);
            }
        }
    }
    cout<<ans<<endl;
    return ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值