NOIP2013TGDay1

本文提供了NOIP2013提高组第一天三道题目的详细解题思路及代码实现,包括转圈游戏、火柴排队和货车运输等问题,通过巧妙的算法设计解决复杂的数据处理挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NOIP2013提高组Day1题解报告


T1 转圈游戏

Description
n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏。按照顺时针方向给 n 个位置编号,从
0 到 n-1。最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……,依此类 推。
游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小
伙伴走到第 m+1 号位置,……,依此类推,第n − m号位置上的小伙伴走到第 0 号位置,第
n-m+1 号位置上的小伙伴走到第 1 号位置,……,第 n-1 号位置上的小伙伴顺时针走到第
m-1 号位置。
现在,一共进行了 10^k 轮,请问 x 号小伙伴最后走到了第几号位置。


解题思路
每个小朋友都要向前走m步,那么每个小朋友的相对位置不变,又由于每次走的步数恒定,所以一定会在某一次出现0号小朋友回到0号位,也就是会出现循环,只要找到循环节,通过快速幂处理10^k,整道题也就轻松解了。

#include<bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i_##END_=(b);i<=i_##END_;++i)
#define REP(i,a,b) for(int i=(a),i_##BEGIN_=(b);i>=i_##BEGIN_;--i)
#define M
int n,m,k,x;
int gcd(int a,int b) {
    if(!b)return a;
    return gcd(b,a%b);
}
int s;
long long Fast(int p) {
    long long res=10;
    long long ans=1;
    while(p) {
        if(p&1) {
            ans=1ll*ans*res%s;
        }
        res=1ll*res*res%s;
        p>>=1;
    }
    return ans;
}
void solve() {
    int tmp=gcd(n,m);
    long long res=1ll*n*m/tmp;
    s=res/m;//循环节长度
    long long b=Fast(k);
    b=1ll*(b*m%n+x)%n;
    printf("%lld\n",b);
}
int main() {
    cin>>n>>m>>k>>x;
    solve();
    return 0;
}

T2 火柴排队

Description
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑(ai −bi)^2(i∈[1,n]),其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数99,999,997 取模的结果。


解题思路
先处理最小距离,可以轻易发现,当两个序列都排好序时,对应的(ai-bi)^2之和最小。
证明:有四个数a,b,c,d,其中b>a,d>c。
生成两个序列
a,b
c,d
则这种情况两个序列的距离是:(a-c)^2+(b-d)^2—->①
我们交换c,d
这种情况两个序列的距离是:(a-d)^2+(b-c)^2—–>②
①-②得2ad+2bc-2ac-bd=2a(d-c)+2b(c-d)=2(a-b)(d-c)
由于a比b小,c比d小,所以2(a-b)(d-c)<0。则①<②。证明出排序后对应是最优的。后面也就简单了,处理最优情况下两序列对应的位置关系,求一下逆序对个数就行了

#include<bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i_##END_=(b);i<=i_##END_;++i)
#define REP(i,a,b) for(int i=(a),i_##BEGIN_=(b);i>=i_##BEGIN_;--i)
#define M 100005
#define md 99999997
int n;
int A[M],B[M];
struct node {
    int x,id;
} C[M],D[M];
int cmp(node a,node b) {
    return a.x<b.x;
}
int Match[M],res[M],ans;
int Bag[M];
void Merge(int L,int R) {
    if(L==R)return;
    int mid=(L+R)>>1;
    Merge(L,mid);
    Merge(mid+1,R);
    int i=L,j=mid+1,a=L;
    while(i<=mid&&j<=R) {
        if(res[j]<res[i]) {
            ans=(ans+mid-i+1)%md;
            Bag[a++]=res[j++];
        } else Bag[a++]=res[i++];
    }
    while(i<=mid)Bag[a++]=res[i++];
    while(j<=R)Bag[a++]=res[j++];
    FOR(i,L,R) {
        res[i]=Bag[i];
    }
}
void solve() {
    FOR(i,1,n)C[i].id=i,C[i].x=A[i];
    FOR(i,1,n)D[i].id=i,D[i].x=B[i];
    sort(C+1,C+n+1,cmp);
    sort(D+1,D+n+1,cmp);
    FOR(i,1,n) {
        Match[D[i].id]=C[i].id;
    }
    FOR(i,1,n) {
        res[Match[i]]=i;
    }
    Merge(1,n);//归并求逆序对个数
    printf("%d\n",ans);
}
int main() {
    cin>>n;
    FOR(i,1,n)scanf("%d",&A[i]);
    FOR(i,1,n)scanf("%d",&B[i]);
    solve();
    return 0;
}

T3 货车运输

Description
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重 量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。


解题思路
构建最大生成树,用倍增+LCA求答案。比较简单的图论题。-_-||
要注意的一点是,整个图不一定是联通的,会被分成几个联通块,把每个联通块都遍历一遍就好了。
直接上代码

#include<bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i_##END_=(b);i<=i_##END_;++i)
#define REP(i,a,b) for(int i=(a),i_##BEGIN_=(b);i>=i_##BEGIN_;--i)
#define M 50005
#define N 10005
int n,m,q;
struct node {
    int u,v,cost;
} edge[M];
struct Edge {
    int to,v;
};
vector<Edge>G[N];
int cmp(node a,node b) {
    return a.cost>b.cost;
}
int Fa[N],Tree[N],sub,Mi[N][16],fa[N][16],dep[N];
void Init() {
    sort(edge+1,edge+m+1,cmp);
    FOR(i,1,n)Fa[i]=i;
}
int getfa(int v) {
    return v==Fa[v]?v:Fa[v]=getfa(Fa[v]);
}
void build() {
    FOR(i,1,m) {
        int u=edge[i].u,v=edge[i].v;
        if(getfa(u)!=getfa(v)) {
            Fa[getfa(u)]=getfa(v);
            G[u].push_back((Edge) {
                v,edge[i].cost
            });
            G[v].push_back((Edge) {
                u,edge[i].cost
            });
        }
    }
}
void dfs(int x,int f) {
    fa[x][0]=f;
    Tree[x]=sub;
    dep[x]=dep[f]+1;
    FOR(i,0,G[x].size()-1) {
        int y=G[x][i].to;
        if(y==f)continue;
        Mi[y][0]=G[x][i].v;
        dfs(y,x);
    }
}
void Dfs() {
    FOR(i,1,n) {
        if(getfa(i)==i) {
            sub++;
            dfs(i,0);
        }
    }
}
void Pre() {
    FOR(j,1,15)FOR(i,1,n) {
        fa[i][j]=fa[fa[i][j-1]][j-1];
        Mi[i][j]=min(Mi[i][j-1],Mi[fa[i][j-1]][j-1]);
    }
}
void Up(int &x,int step) {
    FOR(i,0,15) {
        if(step&(1<<i)) {
            x=fa[x][i];
        }
    }
}
int LCA(int a,int b) {
    if(dep[a]>dep[b])swap(a,b);
    Up(b,dep[b]-dep[a]);
    if(a==b)return a;
    REP(i,15,0) {
        if(fa[a][i]!=fa[b][i]) {
            a=fa[a][i],b=fa[b][i];
        }
    }
    return fa[a][0];
}
void Do() {
    cin>>q;
    int a,b;
    while(q--) {
        scanf("%d%d",&a,&b);
        if(Tree[a]!=Tree[b])puts("-1");
        else {
            int lca=LCA(a,b);
            int step=dep[a]-dep[lca];
            int ans=1e9;
            FOR(i,0,15) {
                if(step&(1<<i)) {
                    if(Mi[a][i]<ans)ans=Mi[a][i];
                    a=fa[a][i];
                }
            }
            step=dep[b]-dep[lca];
            FOR(i,0,15) {
                if(step&(1<<i)) {
                    if(Mi[b][i]<ans)ans=Mi[b][i];
                    b=fa[b][i];
                }
            }
            printf("%d\n",ans);
        }
    }
}
void solve() {
    Init();//并查集预处理
    build();//建树
    Dfs();//遍历每个联通块
    Pre();//倍增的处理
    Do();//求答案
}
int main() {
    cin>>n>>m;
    FOR(i,1,m)scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].cost);
    solve();
    return 0;
}

小结

这份卷不是特别难,我这种蒟蒻考也能AK······
也没什么要注意的地方。(希望今年的题目也都长这样ლ(╹◡╹ლ))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值