[51] (多校聯訓) A層衝刺NOIP2024模擬賽09

HaneDaniko發表於2024-10-19

關於生成式 AI

怎麼才能讓這個 b 學會斷句

我目前的方案是,把逗號和句號單獨作為一個特殊詞彙看待,也統計到詞頻裡,該斷句的時候就斷

表揚這次的題解,寫的很清楚

A.排列最小生成樹

  • 總存在一顆生成樹使得樹上最大邊權值小於 \(n\)

考慮直接連線序列裡的所有 \((i,i+1)\),因為 \(|a_i-a_{i+1}|\lt n\)(由排列的性質),因此 \(w_{\max}\lt((i+1)-i)\times n=n\)

所以我們直接考慮連權值小於 \(n\) 的邊即可

  • 一條邊 \((i,j)\lt n\),則 \(|i-j|\lt \sqrt{n}\)\(|a_i-a_j|\lt\sqrt{n}\)

考慮反證,設 \(|i-j|\ge \sqrt{n}\)\(|a_i-a_j|\ge \sqrt{n}\),考慮最小的情況也有 \(\sqrt{n}\times \sqrt{n}=n\ge n\),因此原命題成立

所以我們只需要考慮使得 \(|i-j|\lt \sqrt{n}\)\(|a_i-a_j|\lt\sqrt{n}\) 的節點即可

因為是排列,可以提前統計每個值所在的位置,然後直接透過值或者位置加減來找對應的點

列舉的這裡有個最佳化(沒啥大用)

  • 由於是無序數對,欽定裡層的變數小於外層變數,這樣可以少一半列舉量和空間浪費

找到了以後直接跑最小生成樹就行了

但是直接這麼跑複雜度是 \(n\sqrt{n}\log n\alpha\) 的,還掛大常數,顯然過不去

剛才我們已經證明了,我們找到的值的值域在 \(n\) 之內,因此我們直接對值域開桶,每次找到數對就存到對應的桶裡,這樣就能避免排序

複雜度 \(n\sqrt{n}\alpha\)

#include<bits/stdc++.h>
using namespace std;
struct dsu{
    int fa[50001];
    void clear(int n){
        for(int i=1;i<=n;++i){
            fa[i]=i;
        }
    }
    int find(int id){
        if(id==fa[id]) return id;
        return fa[id]=find(fa[id]);
    }
    bool connect(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx==fy) return false;
        fa[fx]=fy;
        return true;
    }
}dsu;
int n;
int a[50001];
inline int sol(int i,int j){
    return abs(i-j)*abs(a[i]-a[j]);
}
struct node{
    int i,j;
};
vector<node>q[50001];
int pos[50001];
int vis[50001];
int main(){
    int st=scanf("%d",&n);
    for(int i=1;i<=n;++i){
        st=scanf("%d",&a[i]);
        pos[a[i]]=i;
    }
    dsu.clear(n);
    int tmp=sqrt(n);
    for(int i=1;i<=n;++i){
        for(int j=max(1,i-tmp);j<i;++j){
            if(sol(i,j)<=n){
                q[sol(i,j)].push_back({i,j});
            }
            if(sol(pos[i],pos[j])<=n){
                q[sol(pos[i],pos[j])].push_back({pos[i],pos[j]});
            }
        }
    }
    long long tot=0,ans=0;
    for(int i=1;i<=n;++i){
        for(node u:q[i]){
            if(tot==n-1) break;
            if(dsu.connect(u.i,u.j)){
                ans+=i;
                tot++;
            }
        }
    }
    cout<<ans<<endl;
}

B.卡牌遊戲

先考慮互質怎麼做

n=3 m=4
n 1 2 3 1 2 3 1 2 3 1 2 3
m 1 2 3 4 1 2 3 4 1 2 3 4

透過找規律可以發現,\(m_i=1\) 的所有位置中,對應的 \(n_i=1,2,3\) 恰好都出現過了一遍,其他的 \(m_i\) 也是同理,因此我們直接對 \(n\) 維護有序陣列,對每個 \(m_i\),既然它們面對的對手都是一樣的,那麼也一樣處理,在 \(n\) 的有序陣列裡二分查詢大於,小於,等於 \(m_i\) 的數即可

那麼不互質怎麼做

n=4 m=6 gcd(n,m)=2
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4
1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6

可以發現,\(m_i=1,3,5\) 對應了 \(n_i=1,3\)\(m_i=2,4,6\) 對應了 \(n_i=2,4\)

  • 如果 \(m_i\equiv n_j\pmod {\text{gcd}(n,m)}\),則 \(m_i\) 的對手中有 \(n_j\)

這裡感性理解比較好理解,這裡 \(i\)\(j\) 可以理解成兩個陣列下標差(偏移量),如果兩個數初始偏移量為 \(dx\),每次偏移量均會增加 \(|n-m|\),設 \(n=k_1\text{gcd}(n,m),m=k_2\text{gcd}(n,m)\),進行 \(g\) 輪後的偏移量為 \(g\times |n-m|+dx=g|k_1-k_2|\text{gcd}(n,m)+dx\),可以發現都是在模 \(\text{gcd}(n,m)\) 的意義下同餘的

因此可以拆開,將 \(i\mod \text{gcd}(n,m)\) 的數分為同一組,直接像上面互質的情況那樣分別跑就行了

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int a[100001],b[100001];
vector<int>v[100001];
int ans[3];
int gcd;
signed main(){
    scanf("%lld %lld",&n,&m);
    gcd=__gcd(n,m);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        v[i%gcd].push_back(a[i]);
    }
    for(int i=0;i<=gcd-1;++i) sort(v[i].begin(),v[i].end());
    for(int i=1;i<=m;++i){
        scanf("%lld",&b[i]);
        ans[0]+=tmp*gcd;ans[1]+=tmp2*gcd;ans[2]+=(v[i%gcd].size()-tmp-tmp2)*gcd;
    }
    cout<<ans[1]<<'\n'<<ans[0]<<'\n'<<ans[2]<<'\n';
}

C.位元跳躍

\(S=1\)

考慮怎麼從 \(1\) 完全不花費跳到任意點

  • 如果目標點的二進位制末尾是 \(0\),直接從 \(1\) 跳就不會有任何花費
  • 如果目標點的二進位制末尾是 \(1\),那麼將其取反,反串的二進位制末尾是 \(0\),可以從 \(1\) 跳到其反串,再從反串調到目標點,均不需要花費

但是唯一的特例是形如 \(11111\cdots\) 的點(發現只有當 \(n=2^k\) 的時候,\(2^k-1\) 會出現這種情況,否則可以直接從高位上的點跳走),因為它的反串是 \(0\),而我們並沒有 \(0\) 這個節點,此時我們可以採用下面兩種方式跳到這個點

  • 直接從 \(1\) 跳過去
  • 不用花費地跳到相鄰點,透過相鄰的連邊直接走到目標點

取最小值即可

\(S=2\)

關於異或,可以考慮拆位

  • 列舉 \(i\),每次我們只異或 \(i\) 的其中一位,並且將異或結果與 \(i\) 連邊(因為是無向圖,為了避免重複連邊,可以只列舉 \(i\) 中為 \(1\) 的二進位制位,實測不這麼做會炸空間)
  • 直接跑最短路即可

\(S=3\)

  • 如果 \(i\) 不是 \(j\) 的子集,\(1\rightarrow i\rightarrow j\) 一定不比 \(1\rightarrow j\)

因為你從 \(1\)\(i\) 的貢獻是 \(1\operatorname{or}i\)\(i\)\(j\) 的貢獻是 \(i\operatorname{or}j\),這兩個加和一定包含了 \(1\operatorname{or}j\),並且還會多出來點

因此這麼做

  • 先跑一遍最短路
  • 對每個 \(i\) 列舉其子集,嘗試鬆弛 \(i\)
  • 再跑一遍最短路

這個列舉子集的複雜度太大,具體實現的時候,建一個陣列 \(f_i\) 表示 \(i\) 及其子集的最小 \(dis\),然後列舉和 \(i\) 只差一個二進位制位,並且還是 \(i\) 的子集的數(這些數一定比 \(i\) 小,如果你從小到大列舉 \(i\) 就可以保證這些數一定先被更新過了),用這些數的最小值對 \(i\) 進行鬆弛,同時記得也用這些數更新 \(f_i\),這樣才能實現我們列舉所有子集的效果

要注意的點

  • 第二遍最短路前先把要鬆弛的點入隊(或者你懶得判斷直接入所有點也可以)
  • 初值,\(f_1=0\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,s,k;
struct edge{
    int to,w;
};
vector<edge>e[200001];
int dis[200001];
bool vis[200001];
struct node{
    int id,dis;
    bool operator <(const node&A)const{
        return dis>A.dis;
    }
};
priority_queue<node>q;
namespace subtask1{
    void dij(int s){
        memset(dis,0x3f,sizeof dis);
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;int st=scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                if(s==1){
                    e[i].push_back({j,k*(i&j)});
                    e[j].push_back({i,k*(i&j)});
                }
                if(s==2){
                    e[i].push_back({j,k*(i^j)});
                    e[j].push_back({i,k*(i^j)});
                }
                if(s==3){
                    e[i].push_back({j,k*(i|j)});
                    e[j].push_back({i,k*(i|j)});
                }
            }
        }
        dij(1);
        for(int i=2;i<=n;++i){
            cout<<dis[i]<<" ";
        }
        cout<<endl;
    }
}
bool ispowerof2(int x){
    if(x==1) return true;
    if(x%2!=0) return false;
    return ispowerof2(x/2);
}
namespace subtask2{
    void main(){
        for(int i=2;i<=n;++i){
            cout<<0<<" ";
        }
        cout<<endl;
    }
}
namespace subtask3{
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;int st=scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        int minn=0x7fffffff;
        for(edge i:e[n]) minn=min(minn,i.w);
        for(int i=2;i<=n-1;++i){
            cout<<0<<" ";
        }
        cout<<min(minn,k)<<" ";
        cout<<endl;
    }
}
namespace subtask4{
    void dij(int s){
        memset(dis,0x3f,sizeof dis);
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        for(int i=1;i<=n;++i){
            for(int j=0;j<=31;++j){
                if((i&(1ll<<j))){
                    e[i].push_back({i^(1ll<<j),k*(1ll<<j)});
                    e[i^(1ll<<j)].push_back({i,k*(1ll<<j)});
                }
            }
        }
        dij(1);
        for(int i=2;i<=n;++i){
            cout<<dis[i]<<" ";
        }
    }
}
namespace subtask5{
    int f[200001];
    void dij(int s){
        memset(dis,0x3f,sizeof dis);
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void dij2(int s){
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        for(int i=2;i<=n;++i){
            e[1].push_back({i,k*(1ll|i)});
            e[i].push_back({1,k*(1ll|i)});
        }
        dij(1);
        memset(f,0x3f,sizeof f);
        f[1]=0;
        for(int i=2;i<=n;++i){
            for(int j=0;j<=31;++j){
                if((i&(1ll<<j))){
                    dis[i]=min(dis[i],f[i^(1ll<<j)]+k*i);
                    f[i]=min(dis[i],f[i^(1ll<<j)]);
                }
            }
        }
        for(int i=2;i<=n;++i){
            q.push({i,dis[i]});
        }
        dij2(1);
        for(int i=2;i<=n;++i){
            cout<<dis[i]<<" ";
        }
    }
}
signed main(){
    freopen("jump.in","r",stdin);
    freopen("jump.out","w",stdout);
    // freopen("sample/jump/ex6.in","r",stdin);
    // freopen("out.out","w",stdout);
    int st=scanf("%lld %lld %lld %lld",&n,&m,&s,&k);
    if(n<=800 and m<=800){
        subtask1::main();
        return 0;
    }
    if(s==1 and ispowerof2(n+1)==false){
        subtask2::main();
        return 0;
    }
    if(s==1){
        subtask3::main();
        return 0;
    }
    if(s==2){
        subtask4::main();
        return 0;
    }
    if(s==3){
        subtask5::main();
        return 0;
    }
}

這是什麼

初音未來可愛捏

相關文章