2025CCPC福建邀请赛补题

目录

  • [J 思维 完全平方数](#J 思维 完全平方数)
  • [K 树形DP](#K 树形DP)
  • [C 二分答案 贪心 转化问题](#C 二分答案 贪心 转化问题)
  • [E 前缀和 贪心 查找](#E 前缀和 贪心 查找)

参考@udiandianis 题解
原题

J 思维 完全平方数

最后得到的平方数是什么样的?

  • 对于一般因数p, n o w = x ⋅ p now=x\cdot p now=x⋅p,难于确定+p后 n o w + p = ( x + 1 ) ⋅ p now+p=(x+1)\cdot p now+p=(x+1)⋅p, x + 1 x+1 x+1有什么因数,再找的话需要 O ( x ) O(\sqrt{x}) O(x ),大概是1e6的规模
  • 而且也难以确定选哪个因数进行操作才能使操作数<=100

注意 2 2 k 2^{2k} 22k也是一种平方数,而且 2 2 k − 1 + 2 2 k − 1 = 2 2 k 2^{2k-1}+2^{2k-1}=2^{2k} 22k−1+22k−1=22k,加因数很容易得到

cpp 复制代码
int lowbit(int x){return x&(-x);}
void solve(){
   int n;cin>>n;
   /*
      int tp=n;
      vector<int>p;
      forr(i,2,sqrt(n)){
         if(tp%i==0){
            p.push_back(i);
            while (tp%i==0)tp/=i;
         }
         if(tp==1)break;
      }
      if(tp!=1)p.push_back(tp);
      注意是正约数 一开始只局限于质因数中
      加的操作 对2^x 可以看作进一位
      所以最后的完全平方数是2^2k的形式很好实现
   */
   int tp=n;
   int sq=sqrt(tp);
   vector<int>ans;
   while (sq*sq!=tp)
   {
      int x=lowbit(tp);
      tp+=x;
      ans.push_back(x);
      sq=sqrt(tp);
   }
   cout<<ans.size()<<endl;
   for(auto x:ans)cout<<x<<' ';cout<<endl;
}  

K 树形DP

大致思路是先赋一个点,然后向外拓展。问题在于先赋的点值多少。

需要发现点之间权值的关系性质,框一个先赋点值合法范围。

cpp 复制代码
const int N = 2e5+5,M=1e5;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e18 ,up=1e9;
struct edge{
  int u,v,w;
};
vector<pii>g[N];
int wsm[N],dep[N];
/* 
大致思路是先赋一个点 然后向外拓展
但是注意判断合法不能只靠一个特例,而是有一个合法区间

wsm是常数部分 偶数深度ans_i=wsm_i-x 奇数深度ans_i=wsm_i+x (根节点深度1)
x是第一个赋值的点 可以随便设为点1 1<=x<=1e9 (右半因为w<=1e9)

限制ans_i合法
1<=wsm_i+x<=1e9 -> 1-wsm_i<=x<=1e9-wsm_i 
1<=wsm_i-x<=1e9 -> wsm_i-1e9<=x<=wsm_i-1 
又对x有一个限制
*/
void solve(){
  int n;cin>>n;
  vector<edge>a(n);
  int mn=inf;
  forr(i,1,n-1){
    int u,v,w;cin>>u>>v>>w;
    a[i]={u,v,w};
    g[u].push_back({v,w});
    g[v].push_back({u,w});
    mn=min(mn,w);
  }
  if(n==1){
    yes;cout<<1<<endl;
    return;
  }
  /* 一开始直接取权值最小的一个端点直接赋值为1  wa3
    sort(a.begin()+1,a.end(),[&](edge x,edge y){
      if(x.w==y.w){
        return g[x.u].size()+g[x.v].size()<g[y.u].size()+g[y.v].size();
      }else return x.w<y.w;
    });

    if(mn<=1)return no,void();
    auto dfs=[&](auto &&dfs,int now,int fa)->bool{
      
      for(auto [x,w]:g[now]){
        if(x==fa)continue;
        ans[x]=w-ans[now];
        if(w[x]<=0)return false;
        if(!dfs(dfs,x,now))return false;
      }
      return true;
    };
    int rt;
    if(g[a[1].u].size()<g[a[1].v].size())rt=a[1].u;
    else rt=a[1].v;
    ans[rt]=1;
    if(dfs(dfs,rt,-1)){
      yes;
      forr(i,1,n)cout<<ans[i]<<' ';cout<<endl;
    }else no;
  */
  auto dfs=[&](auto &&dfs,int now,int fa)->void{
    dep[now]=dep[fa]+1;
    for(auto [x,w]:g[now]){
      if(x==fa)continue;
      wsm[x]=w-wsm[now];
      dfs(dfs,x,now);
    }
  };
  dfs(dfs,1,-1);
  int l=1,r=up; //[l,r]是ans_1的范围
  forr(i,1,n){
    if(dep[i]&1){ // ans_奇数=wsm_i+x
      l=max(l,1-wsm[i]);
      r=min(r,up-wsm[i]);
    }else{
      l=max(l,wsm[i]-up);
      r=min(r,wsm[i]-1);
    }
  }
  if(l<=r){// 有合法ans_1
    yes;
    cout<<l<<' ';
    forr(i,2,n){
      if(dep[i]&1)cout<<wsm[i]+l<<' ';
      else cout<<wsm[i]-l<<' ';
    }
    cout<<endl;
  }else no;
}

C 二分答案 贪心 转化问题

一开始想直接找出合并规律,优先合并小的,但是同时考虑数值大小和位置选择太复杂了

中位数只关心相对大小(排名),最后求最大可能值,可以二分答案。数组中大于等于目标答案占多数,最后能合并得到大于目标答案的数,不等关系可以用01表示。

cpp 复制代码
void solve(){
  int n,mx=0;cin>>n;
  vector<int>a(n+1);
  forr(i,1,n)cin>>a[i],mx=max(mx,a[i]);
  /*
  二分 枚举最后可能的答案x 
  贪心 如果最后能化成>=x的数占大部分 则这个答案能化出来 
  */
  auto check=[&](int x)->bool{
    
    vector<int>st;
    
    forr(i,1,n){ // >=x的数设为1 <x设为0
      st.push_back(a[i]>=x);

      if(st.size()>=3){
        string now="000"; // 模拟 对每个三元组判定中位数结果
        int cnt=0;
        reforr(j,0,2){
          cnt+=st.back();
          now[j]=char(st.back()+'0'); // now是后三个的正序
          st.pop_back();
        }

        if(cnt>=1){
          /*
            cnt=3 111 
            cnt=2 110 011 101 
            以上 中位数合并后为1
            cnt=1 100 001 010 合并后得0 损失一个1

            尽量让0不连续 会让合并后中位数为1 优先合并000
            100 可能之后再出现一个0 形成1000 合并成10 不用损失1
            001 前面000已经统统合并了 必是00连续 直接合并001 消除这个连续
            010 和100同理 后面可能再产生000
          */ 
         // 合并
          if(now=="001")st.push_back(0); 
          else{ // 其他的放回
            forr(j,0,2){
              st.push_back(now[j]-'0');
            }
          }
        }else st.push_back(0); // cnt=0 000
      }
    }

    int cnt1=0;
    for(auto i:st){
      cnt1+=i;
    }
    return cnt1>st.size()-cnt1;
  };
  int l=1,r=mx,ans;
  while (l<=r)
  {
    int mid=(l+r)>>1;
    if(check(mid))ans=mid,l=mid+1;
    else r=mid-1;
  }
  cout<<ans<<endl;
}

E 前缀和 贪心 查找

总和不变获得和较小的一组,其实就是想让两组的和接近

需要发现插入和抽出对得分的影响:之后的奇偶位置交换

cpp 复制代码
void solve(){
  int n;cin>>n;
  vector<int>a(2*n+1),odd(n+1,0),even(n+1,0);
  int sm=0;
  forr(i,1,2*n){
    cin>>a[i];
    sm+=a[i];
    
    // int id=(i+1)/2;
    int id;
    if(i&1)id=(i+1)/2,odd[id]=odd[id-1]+a[i];
    else id=i/2,even[id]=even[id-1]+a[i];
  }

  int dis=odd[n]-even[n],mndis=abs(dis);// 初始 even odd位置的差值 sm=2*ans+dis ans=(sm-dis)/2
  set<int>out[2];
  forr(i,1,2*n){// 抽出/插入i位置的牌 i+1往后奇偶性反转 (一开始读假题以为是交换两张牌)
    /*
    dis=pre_odd+suf_odd-pre_even-suf_even 
    从j(j<i)位置抽出 插入i后一个位置
    i、j同奇偶性 1 2 3 4 => 1 2 4 抽出3 => 1 3 2 4 插入1后
    ndis=abs(( odd_j + even_i-even_j + odd_n-odd_i )-( even_j + odd_i-odd_j + even_n-even_i ))
        =abs(( odd_n-even_n )+ 2*odd_j - 2*even_j - 2*odd_i + 2*even_i)
        =abs( dis + 2*( odd_j - even_j ) - 2*( odd_i - even_i ))
        =abs((dis - 2*( odd_i - even_i ))- 2*( even_j -odd_j ))
    */
    // int id=(i+1)/2;
    // int ndis=dis-2*(odd[id]-even[id]);// 插入i后
    int ndis=dis-2*(odd[(i+1)/2]-even[i/2]);// 插入i后
    auto x=out[i%2].lower_bound(ndis);// 找差值最小
    if(x!=out[i%2].end())mndis=min(mndis,abs(ndis-*x));
    if(x!=out[i%2].begin())mndis=min(mndis,abs(ndis-*(--x)));// 最小差值也可能出现在前面
    // cout<<mndis<<endl;
    out[i%2].insert(2*(even[i/2]-odd[(i+1)/2]));
  }
  cout<<(sm-mndis)/2<<endl;
}
相关推荐
松☆1 天前
C++ 算法竞赛题解:P13569 [CCPC 2024 重庆站] osu!mania —— 浮点数精度陷阱与 `eps` 的深度解析
开发语言·c++·算法
kronos.荒1 天前
图论——求孤岛面积、淹没孤岛(python)
python·深度优先·图论
jr-create(•̀⌄•́)1 天前
正则化和优化算法区别
pytorch·深度学习·神经网络·算法
li星野1 天前
刷题:数组
数据结构·算法
tankeven1 天前
HJ182 画展布置
c++·算法
CS_Zero1 天前
无人机路径规划算法——EGO-planner建模总结—— EGO-planner 论文笔记(一)
论文阅读·算法·无人机
杰梵1 天前
聚酯切片DSC热分析应用报告
人工智能·算法
@BangBang1 天前
leetcode (4): 连通域/岛屿问题
算法·leetcode·深度优先
Ulyanov1 天前
像素迷宫:路径规划算法的可视化与实战
大数据·开发语言·python·算法
Mr_pyx1 天前
【LeetCode Hot 100】 除自身以外数组的乘积(238题)多解法详解
算法·leetcode·职场和发展