2025ICPC南昌邀请赛vp补题

在K上卡了很久,感觉很需要积累技巧
参考@Sicko的题解
参考@muschuang123的题解

K 枚举 贪心 策略 思维

最优大致思路是先用操作1把石像都转到同一个方向,然后操作2一起转到正面

当时卡在两个地方

  • 怎么转到同一个方向
  • 转到同一方向面向哪里

操作1的理解:

  • 设每个方向的石像数量是 x i x_i xi,先后选中 i 、 i + 1 i、i+1 i、i+1方向的一个石像进行操作1
  • 可见,原来 x i x_i xi集合中的石像可以慢慢转移到原来 x i − 1 x_{i-1} xi−1集合中,可以看作 i i i方向的石像可以逆向转到 i − 1 i-1 i−1方向,需要 b ( 原 x i ) b(原x_i) b(原xi)个操作1。把所有的转到同一方向,原 x i + 2 、 x i + 3 x_{i+2}、x_{i+3} xi+2、xi+3分别需要2、3倍的操作,相加可得出转到同一方向的操作数
  • 一个石像转4下会转到原方向,所以操作2最多用3次,可以对重复的转圈取模
cpp 复制代码
void solve(){
   int n;cin>>n;
   vector<int>a(n+1),x(5,0);
   forr(i,1,n){
      cin>>a[i];
      x[a[i]]++;
   }
   vector<int>cnt(5,0);
   forr(i,0,3){// 枚举每个方向找最小操作1数
      forr(j,1,n){
         cnt[i]=x[(i+1)%4]+x[(i+2)%4]*2+x[(i+3)%4]*3;// 转到同一方向所需步数
      }
   }
   int ans=inf;
   forr(i,0,3){
      ans=min(ans,cnt[i]+(4-(cnt[i]+i)%4)%4);// 转到同一方向面向(i+cnt[i])%4 再转到0
   }
   cout<<ans<<endl;
}  

另一种理解

按住一个石像转 1 次 + 按住自己转 3 次 = 把一个石像往反方向转 1 次

cpp 复制代码
const int N=1e6+8;
int a[N],f[4];
signed main(){
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	
	for(int i=0;i<4;i++){
		for(int j=1;j<=n;j++){
			f[i]+=(a[j]-i+4)%4;// 反向转次数
		}
	}
	int minn=1e9;
	for(int i=0;i<4;i++){
		minn=min(minn,f[i]+(f[i]*3+4-i)%4);
	}
	cout<<minn<<"\n";
	return 0;
}

D 思维 几何

题意:找一个垂直于坐标轴的平面,找最多能和多少长方体内给定直线交

因为是垂直于坐标轴,可以只看一维轴,平面就是轴上一个点,长方体内直线就是轴上一个区间,抽象为轴上一个点最多能在多少区间内

cpp 复制代码
/*
从左向右枚举左端点l  之前放入的r'>l 那么就和之前的这条线相交   
*/
int cal_insec(vector<pii>a){
    sort(a.begin(),a.end());// 左端点从小到大
    priority_queue<int,vector<int>,greater<int>>rq;
    int mx=0;
    for(auto [l,r]:a){
        while (rq.size()&&rq.top()<l)rq.pop();
        mx=max(mx,(int)rq.size()+1);
        rq.push(r);
    }
    return mx;
}
void solve(){
    int n,a,b,c;cin>>n>>a>>b>>c;
    vector<pii>x,y,z;
    forr(i,1,n){
        int x1,y1,z1,x2,y2,z2;
        cin>>x1>>y1>>z1>>x2>>y2>>z2;
        if(x1>x2)swap(x1,x2);
        if(y1>y2)swap(y1,y2);
        if(z1>z2)swap(z1,z2);
        // 三维转一维线段 找一维线段相交
        x.push_back({x1,x2});
        y.push_back({y1,y2});
        z.push_back({z1,z2});
    }
    // cout<<cal_insec(x)<<endl<<cal_insec(y)<<endl<<cal_insec(z)<<endl;
    cout<<max({cal_insec(x),cal_insec(y),cal_insec(z)})<<endl;
}

F 贪心 化式子

cpp 复制代码
void solve(){
    int n,k;
    double P,L,R;
    cin>>n>>k;
    vector<double>c(n+1,0),r(n+1,0);
    cin>>r[0]>>c[0]>>P>>L>>R;

    forr(i,1,k){
        int p;double v;
        cin>>p>>v;
        r[p]=v;
    }
    /*
        c_i=p*c_{i-1}+(1-p)*r_{i-1}
        两项系数统一 c_{i-1}-c_i=(1-p)*c_{i-1}-(1-p)*r_{i-1}=(1-p)*(c_{i-1}-r_{i-1})
        c_i-r_i=(c_i-c_{i+1})/(1-p)
        1~n求和得(c_1-c_{n+1})/(1-p) c_{n+1}越小越好 即不指定的r 越小越好
    */
    double ans=0;
    forr(i,1,n){
        if(r[i]==0)r[i]=L;
        c[i]=P*c[i-1]+(1-P)*r[i-1];
        ans+=c[i]-r[i];
    }
    cout<<fixed<<setprecision(8)<<ans<<endl;
}

G dp

题意:至少有多少连续通道权值乘积>x

cpp 复制代码
const int N = 2e5+10,M=1e5;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e9+10 ;
// struct trans
// {
//     int u,v,d;
// };
vector<pii>g[N];
int dp[N][35];// dp[i点出发][经过j条边]=最大权值乘积
void solve(){
    int n,m,q;cin>>n>>m>>q;
    forr(i,1,n)forr(j,0,31)dp[i][j]=1;
    // forr(i,1,m){
    //     int u,v,d;cin>>u>>v>>d; 应该按点存边更新 每次遍历更新每个点
    //     forr(j,1,30){
    //         dp[u][j]=max(dp[u][j],dp[u][j-1]*d);
    //     }
    // }
    /* 
        发现d取值至少是2,x<=1e9,那么单次询问经过的边数一定不会多于30条
        最多用30条路 把所有的路更新30次
    */
    // vector<trans>a(n+1);
    forr(i,1,m){
        int u,v,d;cin>>u>>v>>d;
        g[u].push_back({v,d});
    }
    forr(j,0,31){
        forr(i,1,n){
            for(auto [x,d]:g[i]){
                dp[i][j+1]=max(dp[i][j+1],min(inf,dp[x][j]*d));// 从一个起点转移出边
            }
        }
    }

    forr(i,1,q){
        int p,x;cin>>p>>x;
        int pos=upper_bound(dp[p],dp[p]+32,x)-dp[p];
        if(x/dp[p][pos]==0)cout<<pos<<endl;
        // 另一种枚举找法
        // forr(pos,1,30){
        //     if(x/dp[p][pos]==0){
        //         cout<<pos<<endl;
        //         break;
        //     }
        // }
    }
}

I 双指针 组合计数 容斥

k 是确定的,这提示我们使用双指针。

每个区间修改后1的数量不变, [ l , r ] [l,r] [l,r]区间贡献是 C r − l + 1 k C_{r-l+1}^k Cr−l+1k,中间会有和其他区间重复的贡献,就是和其他区间重叠的1数量不变的部分

cpp 复制代码
const int N = 1e5,M=1e5;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e9+10 ;
int fac[N+10],ifac[N+10];
int qpow(int a,int b,int p){
    int res=1;
    while (b)
    {
        if(b&1)(res*=a)%=p;
        b>>=1;
        (a*=a)%=p;
    }
    return res%p;
}
int inv(int x){
    return qpow(x,mod-2,mod)%mod;
}
void init(){
    fac[0]=1;
    forr(i,1,N){
        fac[i]=fac[i-1]*i%mod;
    }
    ifac[N]=inv(fac[N]);
    reforr(i,0,N-1){
        ifac[i]=ifac[i+1]*(i+1)%mod;
    }
}
int C(int n,int m){
    return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
void solve(){
    int n,k;
    string s;cin>>n>>k>>s;
    s=' '+s;
    /*
        双指针 拓展最长的有k个1的段
        任意取的最长段 1的数量都不变
    */
    vector<int>pre(n+1,0);
    forr(i,1,n){
        pre[i]=pre[i-1]+(s[i]-'0');
    }
    if(pre[n]<k)return cout<<0<<endl,void();
    int ans=0;
    if(k==1){
        vector<int>cnt;
        int z=0;
        forr(i,1,n){
            if(s[i]=='1'){
                cnt.push_back(z);
                z=0;
                if(cnt.size()>1){
                    int tp=cnt.size();
                    ans+=cnt[tp-1]+cnt[tp-2];
                }
            }else z++;
        }
        cnt.push_back(z);
        int tp=cnt.size();
        ans+=cnt[tp-1]+cnt[tp-2]+1;
    }else{
        int nowl=1,nowr=0,lstr=-1;
        while (nowr<n)
        {
            while(nowr<n&&pre[nowr+1]-pre[nowl-1]<=k)nowr++;
            if(pre[nowr]-pre[nowl-1]<k)break;// 判断区间1的个数
            int lap=0;// 和上一段重叠部分
            if(lstr>=nowl)lap=C(lstr-nowl+1,pre[lstr]-pre[nowl-1]);// nowl~lstr这一段的1数量不变 就是两段都会算的部分
            (ans+=C(nowr-nowl+1,pre[nowr]-pre[nowl-1])-lap+mod)%=mod;
            while(nowl<=nowr&&pre[nowr]-pre[nowl-1]>=k)nowl++;// 摆脱1数量=k的这一段
            lstr=nowr;
        }
    }
    cout<<ans<<endl;
}
相关推荐
发发就是发2 小时前
I2C适配器与算法:从一次诡异的时序问题说起
服务器·驱动开发·单片机·嵌入式硬件·算法·fpga开发
啊哦呃咦唔鱼2 小时前
leetcode二分查找
数据结构·算法·leetcode
郝学胜-神的一滴2 小时前
[ 力扣 1124 ] 解锁最长良好时段问题:前缀和+哈希表的优雅解法
java·开发语言·数据结构·python·算法·leetcode·散列表
戴西软件2 小时前
戴西CAxWorks.VPG车辆工程仿真软件|假人+座椅双调整 汽车仿真效率直接拉满
java·开发语言·人工智能·python·算法·ui·汽车
Tairitsu_H2 小时前
C++入门指南:从基础语法到核心特性全解析
c++·算法·基础
programhelp_2 小时前
2026 高盛(Goldman Sachs)Coding Interview 真题分享|Design HashMap + 其他面试题完整解析
算法·哈希算法
Pentane.2 小时前
力扣HOT100:T.1 两数之和|循环遍历算法笔记及打卡(12/100)
c++·笔记·算法·leetcode
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:士兵站队
c++·算法·贪心算法·csp·信奥赛·线性扫描贪心·士兵战队
无限进步_2 小时前
二叉树的中序遍历(非递归实现)
开发语言·数据结构·c++·windows·算法·visual studio