2025“钉耙编程”中国大学生算法设计暑期联赛(6)

cat学乘法

#include<bits/stdc++.h> 
using namespace std;
#define int long long
int n;
int a[200010];
/*
10
5
-1 -2 -3 0 1
*/
signed main( )
{

    std::ios::sync_with_stdio(false);
    int T;cin>>T;
    while(T--){
         cin>>n;
    int cnt_0=0ll;int cnt0=0ll;int mn=1e10;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]==0ll)cnt_0++;
        else if(a[i]<0ll){cnt0++;mn=min(mn,-a[i]);}
        else if(a[i]>0){mn=min(mn,a[i]);}
    }
    if(cnt_0!=0ll){
        cout<<cnt_0<<'\n';
    }else {
        if(cnt0&1){
            cout<<mn+1<<'\n';
        }else cout<<0<<'\n';
    }
   
    }
   
  
}

对撞器

a[1]或a[n],2~n-1都可以贡献a[1]
中间,2~pos-1 pos+1~n-1可以贡献a[pos]
学习多个最大值处理,相等的都加到vec,头和尾可能是1,n

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=200010;
int n;
int a[N];

signed main(){
  int T;cin>>T;
  while(T--){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];

    if(n<=2){
      cout<<0<<'\n';continue;
    }

    int mx=a[1];for(int i=1;i<=n;i++)mx=max(mx,a[i]);
    vector<int> vec;for(int i=1;i<=n;i++){if(a[i]==mx)vec.push_back(i);}

    if(*vec.begin()==1||vec.back()==n){
      cout<<(n-2)*mx<<'\n';
    }else {
  cout<<(n-3)*mx+max(a[1],a[n])<<'\n';
    }

  }
}

传送排序

留下来没有用过传送的数构成一个上升子序列
f[i]表示以p[i]为结尾的留下的子序列 需要的操作数量
考虑这个子序列上一个元素p[j],如果p[j]=p[i]-1那就正是下一个元素无需操作,f[i]=f[j]
如果p[j]<p[i]-1那就需要现在把本来在之间的p[i]-p[j]-1个元素插进去,f[i]=f[j]+p[i]-p[j]-1+1

由于开头和结尾均可插入,添加0和n+1也作为元素
第二种操作用树状数组维护1<=j<=i-1 f[j]-p[j]最小值,由于j=0 f[j]-j=f[0]-0=0用树状数组差不到,需要额外拿出来

#include<iostream>
using namespace std;
#define INF 0x3f3f3f3f
const int N=200010;
int n;
int tr[N];
int p[N];
int b[N];
int f[N];
int lowbit(int x){
    return x&(-x);
}
void add(int x,int k){
    int cnt=0;
    for(int i=x;i<=n+1;i+=lowbit(i)){
     
        tr[i]=min(tr[i],k);
    }
}
int query(int x){
    int res=INF;
    for(int i=x;i>0;i-=lowbit(i))res=min(res,tr[i]);
    return res;
}

int main(){
    int T;cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++){cin>>p[i];b[p[i]]=i;tr[i]=INF;f[i]=INF;}
        p[0]=0;p[n+1]=n+1;b[0]=0;b[n+1]=n+1;tr[0]=0;tr[n+1]=INF;f[0]=0;f[n+1]=INF;

    
        for(int i=0;i<=n+1;i++){
            if(i==0){
                f[i]=0;
                continue;
            }
        f[i]=min(f[i],f[b[p[i]-1]]);
        if(p[i]>2){int t=query(p[i]-2);f[i]=min(f[i],t+p[i]);}
        f[i]=min(f[i],0+p[i]);
 
        add(p[i],f[i]-p[i]);
        }
        /*  for(int i=0;i<=n+1;i++){
            cout<<f[i]<<" ";
        }cout<<endl; */
      
        cout<<f[n+1]<<'\n';
    }
}

cats 的 max

状压dp第二类
第一排O((1<<m)nm)
转移部分 每一列分别在sta,sta0,stasta0里,有3m种 O(3^mkk)

#include<iostream>
using namespace std;
#define int long long
int n,m,k;
int f[(1<<13)+10][20];//已经产生最大值的列集合 已经选的行个数
int a[1010][20];

signed main(){
    std::ios::sync_with_stdio(false);
    int T;cin>>T;
    while(T--){
        cin>>n>>m>>k;
        for(int i=1;i<=n;i++){
            for(int j=0;j<m;j++){
                cin>>a[i][j];
            }
        }
        if(k>=m){
            int ans=0;
            for(int j=0;j<m;j++){
                int mx=0;
                for(int i=1;i<=n;i++){
                    mx=max(mx,a[i][j]);
                }
                ans+=mx;
            }
            cout<<ans<<'\n';continue;
        }

        for(int sta=1;sta<(1<<m);sta++)for(int k1=0;k1<=k;k1++)f[sta][k1]=0;
        for(int sta=1;sta<(1<<m);sta++){
            for(int i=1;i<=n;i++){
                int tem=0;
                for(int col=0;col<m;col++){
                    if(sta&(1<<col)){
                        tem+=a[i][col];
                    }
                }
                f[sta][1]=max(f[sta][1],tem);
            }
        }

        //用子集和它的补集
        //子集必然全算完了
        int ans=0;
        for(int sta=1;sta<(1<<m);sta++){
            for(int k1=1;k1<=k;k1++){
               for(int sta0=sta;sta0;sta0=(sta0-1)&sta){
                    for(int k0=0;k0<=k1;k0++){
                        // if(sta==15&&k1==2&&sta0==11&&k0==1){
                        //     cout<<"get"<<f[sta][k1]<<" "<<f[sta0][k0]<<" "<<f[sta^sta0][k1-k0]<<'\n';
                        // }
                        f[sta][k1]=max(f[sta][k1],f[sta0][k0]+f[sta^sta0][k1-k0]);
                       ans=max(ans,f[sta][k1]);
                    }
                } 
            }
        }
    
        cout<<ans<<'\n';

    }
}

取模

随机化

#include<bits/stdc++.h>
#include <random>
#include <chrono>
using namespace std;
const int N=500010;
int n,m,c;
int a[N];
int b[N];
std::mt19937 rd(std::chrono::system_clock::now().time_since_epoch().count());

bool check(int k){
    for(int i=1;i<=min(5,n);i++){
        int tmp=0;
        for(int j=a[i]%k;j<=m;j+=k){//a[i]所在剩余类
            tmp+=b[j];
        }
        if(tmp!=n/c&&tmp!=0)return 0;
    }

    for(int i=0;i<k;i++){
        int tmp=0;
        for(int j=i;j<=m;j+=k){
            tmp+=b[j];
        }
        if(tmp!=n/c&&tmp!=0)return 0;
    }
    return 1;
}

signed main(){
    std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  int T;cin>>T;
  while(T--){
    cin>>n>>m>>c;
    for(int i=0;i<=m;i++)b[i]=0;
    for(int i=1;i<=n;i++){cin>>a[i];b[a[i]]++;}
  //  shuffle(a + 1, a + 1 + n, rd);

    if(check(m+1)){
        cout<<-1<<'\n';continue;
    }

    vector<int> ans;
    for(int i=1;i<=m;i++){
        if(check(i))ans.push_back(i);
    }
  
    cout<<ans.size()<<' ';
    for(auto x:ans){
        cout<<x<<' ';
    }cout<<'\n';

  }
}
posted @ 2025-08-06 18:02  arin876  阅读(26)  评论(0)    收藏  举报