2025杭电多校第三场 三带一、性质不同的数字、核心共振 个人题解

核心共振

前缀和 #数学 #曼哈顿距离 #切比雪夫距离

题目

image

思路

切比雪夫距离可以和曼哈顿距离互相转化!!

直接上数学推导!

先化简题目要求的式子:

\[\begin{align} 定义:F(i,j)&=max\{ |x_{i}-x_{j}|,|y_{i}-y_{j}| \}\\ \\ ans&=\sum_{1\leq i< j\leq n}(a_{i}+a_{j})F(i,j)\\ \\ &=\sum_{1\leq i< j\leq n}a_{i}F(i,j)+\sum_{1\leq i< j\leq n}a_{j}F(i,j)\\ \\ &=\sum_{1\leq i< j\leq n}a_{i}F(i,j)+\sum_{1\leq j< i\leq n}a_{i}F(j,i)\\ \\ &而F(i,j)=F(j,i)\\ \\ \therefore 原式&=\sum_{1\leq i< j\leq n}a_{i}F(i,j)+\sum_{1\leq j< i\leq n}a_{i}F(i,j)\\ \\ &=\sum_{1\leq i,j\leq n,i\neq j}a_{i}F(i,j) \end{align} \]

将切比雪夫距离转化成曼哈顿距离:

\[\begin{align} &令u=x+y,v=x-y\\ \\ &则F(i,j)=max\{ |x_{i}-x_{j}|,|y_{i}-y_{j}| \}\\ \\ &=\frac{1}{2}max\{ |x_{i}+y_{i}-(x_{j}+y_{j})+x_{i}-y_{j}-(x_{i}-y_{j})|,|x_{i}+y_{i}-(x_{j}+y_{j})-[x_{i}-y_{j}-(x_{i}-y_{j})] |\}\\ \\ &=\frac{1}{2}max\{ |u_{i}-u_{j}+v_{i}-v_{j}|,|u_{i}-u_{j}-(v_{i}-v_{j})| \}\\ \\ 而max&\{ |a+b|,|a-b| \}=|a|+|b|\\ \\ \therefore原式&=\frac{1}{2}(|u_{i}-u_{j}|+|v_{i}-v_{j}|)\\ \\ \therefore ans&=\sum_{1\leq i,j\leq n,i\neq j}a_{i}F(i,j)=\frac{1}{2}\sum_{1\leq i,j\leq n,i\neq j}a_{i}(|u_{i}-u_{j}|+|v_{i}-v_{j}|)\\ \\ &=\frac{1}{2}\sum_{1\leq i,j\leq n,i\neq j}a_{i}|u_{i}-u_{j}|+\frac{1}{2}\sum_{1\leq i,j\leq n,i\neq j}a_{i}|v_{i}-v_{j}| \end{align} \]

将所求式子转化成前缀和:

\[\begin{align} &对于u轴而言:\\ \\ &\sum_{1\leq i,j\leq n,i\neq j}a_{i}|u_{i}-u_{j}|=\sum_{1\leq i\leq n}a_{i}\left[ \sum_{1\leq j<i}(u_{i}-u_{j})+\sum_{i< j\leq n}(u_{j}-u_{i}) \right]\\ \\ &=\sum_{1\leq i\leq n}a_{i}\left[ (i-1)u_{i}-\sum_{1\leq j<i}u_{j}+\sum_{i< j\leq n}u_{j}-(n-i)u_{i} \right]\\ \\ &定义:upre[i]=\sum_{1\leq k\leq i}u_{k}\\ \\ 则原式 &=\sum_{1\leq i\leq n}a_{i}\left[ \ (2i-1-n)u_{i}+upre[n]-upre[i]-upre[i-1]\ \right]\\ \\ &v轴答案 可以同理得出,二者相加除二即可。 \end{align} \]

代码实现

#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i++)
#define per(i, a, b) for(ll i = (a); i >= (b); i--)
#define mid ((l+r)>>1)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';

const int mod=1e9+7;
int n;
const int N=2e5+5;
vector<pair<ll,ll>> u,v;
ll upre[N],vpre[N],inv2;
ll qpow(ll a, ll b){
    ll res = 1;
    while (b){
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

void solve() {
    cin>>n;
    u.clear(),v.clear();
    u.reserve(n+1),v.reserve(n+1);
    u.push_back({0,0}),v.push_back({0,0});
    rep(i,1,n){
        int x,y,a;cin>>x>>y>>a;
        u.push_back({x+y,a}),v.push_back({x-y,a});
    }
    sort(u.begin()+1,u.end()),sort(v.begin()+1,v.end());
    rep(i,1,n){
        upre[i]=(u[i].first+upre[i-1])%mod;
        vpre[i]=(v[i].first+vpre[i-1])%mod;
    }
    ll ans=0;
    rep(i,1,n){
        ll mul=(((2*i-1-n)*u[i].first+mod)%mod+upre[n]-upre[i]-upre[i-1]+2*mod)%mod;
        ans+=(u[i].second*mul)%mod;
        ans%=mod;
        mul=(((2*i-1-n)*v[i].first+mod)%mod+vpre[n]-vpre[i]-vpre[i-1]+2*mod)%mod;
        ans+=(v[i].second*mul)%mod;
        ans%=mod;
    }
    ans=(ans*inv2)%mod;
    cout<<ans<<'\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    inv2=qpow(2,mod-2);
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

性质不同的数字

哈希 #扫描线 #差分

题目

image

思路

将每个线段编号,观察数轴上的位置\(pos\)\(pos\)这个位置上所有覆盖的线段所构成的集合记为其状态,比如在\(pos=3\)的位置上有编号为\(\{ 1,3,4 \}\)的三条线段覆盖,\(pos=3\)的状态即为\(\{ 1,3,4 \}\)

题目所求即整个数轴上有多少种不同的状态。

我们可以对每条线段进行随机编号,这样可以保证后续哈希的过程产生碰撞的概率极小。

在输入线段\([l,r]\)的时候在\(l\)\(r+1\)位置打上差分标记

随后以哈希值\(hash\)作为扫描线一路还原过去,开一个哈希表\(unordered\_map<ll,bool>cnt\)记录不同状态数

最后输出状态总数\(cnt.size()\)即可

代码实现

#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<unordered_map>
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i++)
#define per(i, a, b) for(ll i = (a); i >= (b); i--)
#define mid ((l+r)>>1)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';

const int N=2e5+5;
const ll mod=1145141919810;
unordered_map<ll,bool>cnt;
unordered_map<ll,bool>vis;
struct line{
    vector<ll>l,r;
};
map<int,line>mp;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

void solve() {
    cnt.clear();mp.clear();vis.clear();
    int n;cin>>n;
    rep(i,1,n){
        int l,r;cin>>l>>r;
        ll idx=rng();
        while(vis[idx])idx=rng();
        vis[idx]=1;
        mp[l].l.push_back(idx);
        mp[r+1].r.push_back(idx);
    }
    if(n==0){cout<<1<<'\n';return;}
    ll hash=0;
    for(auto&lin:mp){
        for(auto&ele:lin.second.l){
            hash=(hash+ele)%mod;
        }
        for(auto&ele:lin.second.r){
            ele%=mod;
            hash=(hash-ele+mod)%mod;
        }  
        cnt[hash];     
    }
    cout<<cnt.size()<<'\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    // initrd();
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}


三带一

二分 #数学

题目

image

思路

求最大值,考虑二分答案,判断当前答案是否合法。

对于数字\(i\),设配对成功了\(b_{i}\)\(3\),剩余\(c_{i}=a_{i}-3\times b_{i}\)
则有\(3\times b_{i}\leq a_{i}\quad b_{i}\leq \left\lfloor \frac{a_{i}}{3} \right\rfloor\)
\(A=\sum a_{i},ans=\sum b_{i},C=\sum c_{i}=A-3\times ans\)
每二分确定一个\(ans\)\(b_{i}\)都由多条不等式确定:

\[\begin{align} &B\leq C\to ans\leq \left\lfloor \frac{A}{4} \right\rfloor \\ \\ &b_{i}+c_{i}\leq C且b_{i}+c_{i}=a_{i}-2b_{i}\\ \\ &\therefore b_{i}\geq \left\lceil \frac{a_{i}-A+3ans}{2} \right\rceil \end{align} \]

因此对于所有的\(ans\),只需要满足任意\(i\)都有

\[\left\lceil \frac{a_{i}-A+3ans}{2} \right\rceil \leq b_{i}\leq \left\lfloor \frac{a_{i}}{3} \right\rfloor \]

即为一种合法答案。

为何不等式\(b_{i}+c_{i}\leq C\)为合法的充要条件?
\(n=3\)时的情况为例:

\[\begin{align} &设\begin{cases} b_{1}+c_{1}<C\\ \\ b_{2}+c_{2}<C \\ \\ b_{3}+c_{3}<C \end{cases} \ 为初始状态\\ \\ &假设从数字1中取一个b与数字2的c配对: \\ \\ &\begin{cases} b_{1}-1+c_{1}<C-1\\ \\ b_{2}+c_{2}-1<C-1 \\ \\ b_{3}+c_{3}\ ?\ C-1 \end{cases}\\ \\ &由于数字都为整数,数字3的不等式分两种情况\\ &b_{3}+c_{3}<C-1或b_{3}+c_{3}=C-1\\ \\ &情况一等价于:\\ &\begin{cases} b_{1}'+c_{1}<C'\\ \\ b_{2}+c_{2}'<C' \\ \\ b_{3}+c_{3}\ <\ C' \end{cases}\ 这与初始状态形式一致; \\ \\ &情况二等价于:\\ &\begin{cases} b_{1}'+c_{1}<C'\\ \\ b_{2}+c_{2}'<C' \\ \\ b_{3}+c_{3}= C' \end{cases} \ 此时最优策略必然是将b_{3}消耗完全,状态转移为: \\ \\ &\begin{cases} b_{1}<C''\\ \\ b_{2}<C' '\\ \\ c_{3}= C'' \end{cases} \ 随后消耗b_{2}、b_{1},结束过程 \end{align} \]

\[\begin{align} &在n>3的情况下,由于不等式B\leq C\\ \\ &可以证明,上述n条不等式中,任意时刻下最多一条式子取等\\ \\ &因此,只需要保证对任意的i都有b_{i}+c_{i}\leq C,即可保证全局 合法 \end{align} \]

代码实现

#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i++)
#define per(i, a, b) for(ll i = (a); i >= (b); i--)
#define mid ((l+r)>>1)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';

int a[14],A;

bool check(int ans){
    int down=0;
    rep(i,1,13){
        int val=(int)(ceil((1.0)*(a[i]-A+3*ans)/2));
        if(a[i]/3<val)return 0;
        if(val<0)val=0;
        down+=val;
    }
    if(ans<down)return 0;
    return 1;
}

void solve() {
    A=0;
    int up=0;
    rep(i,1,13)cin>>a[i],A+=a[i],up+=a[i]/3;
    int l=0,r=min(A/4,up)+1;
    while(l+1<r){
        if(check(mid)){
            l=mid;
        }else{
            r=mid;
        }
    }
    cout<<l<<'\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)solve();
    return 0;
}

类题:王国——迁移

二分 #数学

题目

image

思路

求最小值,考虑二分答案判断合理性

二分确定一个答案\(ans\),则每个城市能接受的人数\(d_{i}=\left\lfloor \frac{ans}{c_{i}} \right\rfloor\)
\(D=\sum d_{i},B=\sum b_{i}\),则对于所有的\(i\),有不等式:

\[\begin{align} &B\leq D\\ \\ &b_{i}\leq D-d_{a_{i}} \end{align} \]

原理与上题相同

代码实现

#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i++)
#define per(i, a, b) for(ll i = (a); i >= (b); i--)
#define mid ((l+r)>>1)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';

const int N=1e6+5;
ll a[N],b[N],c[N],d[N],B,n,D;
bool check(ll ans){
    rep(i,1,n){
        d[i]=ans/c[i];
        D+=d[i];
    }
    if(D<B)return 0;
    rep(i,1,n){
        if(b[i]+d[a[i]]>D)return 0;
    }
    return 1;
}

void solve() {
    B=0;
    cin>>n;
    rep(i,1,n)cin>>a[i];
    rep(i,1,n)cin>>b[i],B+=b[i];
    rep(i,1,n)cin>>c[i];
    ll l=0,r=1e12;
    while(l+1<r){
        D=0;
        if(check(mid))r=mid;
        else l=mid;
    }
    cout<<r<<'\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)solve();
    return 0;
}
posted @ 2025-07-31 18:45  CUC-MenG  阅读(42)  评论(0)    收藏  举报