HGOI7.9集训题解

题解

今天做的是13年day1的题(我真的觉得好难啊但当年的分数线好高啊orz)

这里写图片描述

第一题——转圈游戏(circle)

【题目描述】

  • 就是说有n(0~n)个小朋友玩转圈圈的游戏。进行 10 k 轮,每轮在0号位置上的小朋友到m位上,1号到m+1上..问进过这么多轮后,原来在x位上的小朋友在哪个位置。

  • 看到 10 k 就果断知道,这是一道数学题,而且果断快速幂。然后取个模,五分钟打完。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
void fff(){
    freopen("circle.in","r",stdin);
    freopen("circle.out","w",stdout);
}
int n,m,k,x;
int quick_(int k){
    if(k==1){
        return 10%n;
    }
    int tmp=quick_(k/2)%n;
    tmp=tmp*tmp%n;
    if(k%2==1) tmp=tmp*(10%n)%n;
    return tmp%n;
}
int main(){
    fff();
    scanf("%d%d%d%d",&n,&m,&k,&x);
    cout<<((m%n)*(quick_(k)%n)%n+x)%n;
    return 0;
}

第二题——火柴排队(match)

【题目描述】

  • 就是告诉你,有两排乱序的火柴,各自有各自的高度,然后让你求要交换多少次(只能在自己这排交换),使得两排对应差之和最小,这里还给了个公式: i = 1 n ( a i b i ) 2

  • 其实还好这道题。求多少对交换的就知道是要归并求逆序对,而这个公式怎么处理?把平方打开就是 i = 1 n ( a i 2 + b i 2 2 a i b i )
  • 不管怎么样, a i 2 + b i 2 都是不会变的,那就只能让 2 a i b i 尽可能的大吧
  • 然后根据我多年的数学经验,在序列当中越接近的乘机最大,那上下两条序列只要让他们的排列对上就好了咯。
  • 打到这里的思路都还比较好想,接下来就是怎么去求这个排列对上就好了。然后我想了两个序列都排一遍逆序对减一下?然后被样例给毙了。换个思路。
  • 我们如果把序列a和b的序列离散化,再来排一遍,获得每个数的位置分别记为a[i]和b[i],那么排完一遍后我们再来记录一个数组f[],记录当前的a[]所对应的b[i]的位置。(其实这里有点讲不清了,那来段代码)
    struct node{
    int first,second;
    bool operator < (const node x) const {
        return first<x.first;
    }
    }a[MAXN],b[MAXN];
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    for (int i=1;i<=n;i++){
        f[a[i].second]=b[i].second;
    }
  • 这样子就是说离散之后f[i]所对应的应该是i,但现在对的不是i,那就来个排序求个逆序对就可以了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
void fff(){
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
}
const int MAXN=101000;
const int MOD=99999997;
int n;
struct node{
    int first,second;
    bool operator < (const node x) const {
        return first<x.first;
    }
}a[MAXN],b[MAXN];
int f[MAXN],t[MAXN],ans=0;
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,temp=l;
    while (i<=mid&&j<=r){
        if(f[i]<=f[j]){
            t[temp++]=f[i++];
        }else{
            t[temp++]=f[j++];
            ans=(ans+mid-i+1)%MOD;
        }
    }
    while (i<=mid) t[temp++]=f[i++];
    while (j<=r) t[temp++]=f[j++];
    memcpy(&f[l],&t[l],(r-l+1)*sizeof(int));
}
int main(){
    fff();
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i].first);
        a[i].second=i;
    }
    for (int i=1;i<=n;i++){
        scanf("%d",&b[i].first);
        b[i].second=i;
    }
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    for (int i=1;i<=n;i++){
        f[a[i].second]=b[i].second;
    }
    merge(1,n);
    cout<<ans;
    return 0;
}

第三题——货车运输(track)

【题目描述】

  • 有n个城市,m条边进行联通,边上权值是限制重量( 0 l 100000 )。让你求q组数据,从a到b的最大限重(一条路径上的限重是该路径权值的最小值,但由于有多条可联通的路径,所以求的是最大限重)。

  • 这道题看完题目,发现是求最小值的最大值。那果断二分啊!而且二分的效率还贼高 O ( q l o g 2 l ) 。然后我一把行云流水的就把二分打了出来,然后解决是否联通。二分出最大限重,如果路径当中的限重比这个小就不能走这条路,否则是可以走的,来把spfa就可以判断是否联通。
  • 但是,很不幸的是,我没有算上每回二分都做spfa的问题,很花时间就超时了orz。骗了十五分。

  • 来讲正解

  • 首先两点之间有多条路径,那么需要把这两点之间的路径最大的保留,剩下的过滤掉,那么就可以靠一把最大生成树来解决。建完树之后就可以来进行树上LCA维护最小路径(这是常规题目了)。
  • 题解说建完树之后还可以用树剖(200行代码),那我是手残么不打四十行的LCA…orz
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#define INF 0x7f7f7f

using namespace std;
void fff(){
    freopen("truck.in","r",stdin);
    freopen("truck.out","w",stdout);
}
const int MAXN=10100;
int n,m;
struct Edge{
    int from,to,val;
    bool operator <(const Edge x) const {
        return val>x.val;
    }
};
vector <Edge> edge,ed;
vector <int> G[MAXN];
bool visited[MAXN];
int qury;
int fa[MAXN];
int find_(int x){
    if(x!=fa[x]) fa[x]=find_(fa[x]);
    return fa[x];
}
void kru(){
    sort(edge.begin(),edge.end());
    int siz=edge.size();
    for (int i=1;i<=n;i++){
        fa[i]=i;
    }
    for (int i=0;i<siz;i++){
        Edge &e=edge[i];
        int a=find_(e.from),b=find_(e.to);
        if(a!=b){
            fa[a]=b;
            ed.push_back(e);
            G[e.from].push_back(ed.size()-1);
            ed.push_back((Edge){e.to,e.from,e.val});
            G[e.to].push_back(ed.size()-1);
        }
    }
}
int deep[MAXN],father[MAXN][20],w[MAXN][20];
void dfs(int u){
    visited[u]=true;
    for (int i=0;i<G[u].size();i++){
        Edge &e=ed[G[u][i]];
        if(visited[e.to]) continue;
        deep[e.to]=deep[u]+1;
        father[e.to][0]=u;
        w[e.to][0]=e.val;
        dfs(e.to);
    }
}
void sett(){
    for (int j=1;j<20;j++){
        for (int i=1;i<=n;i++){
            father[i][j]=father[father[i][j-1]][j-1];
            w[i][j]=min(w[i][j-1],w[father[i][j-1]][j-1]);
        }
    }
}
int lca(int x,int y){
    if(find_(x)!=find_(y)){
        return -1;
    }
    int ans=INF;
    if(deep[x]>deep[y]) swap(x,y);
    for (int i=19;i>=0;i--){
        if(deep[father[y][i]]>=deep[x]){
            ans=min(ans,w[y][i]);
            y=father[y][i];
        }
    }
    if(x==y) return ans;
    for (int i=19;i>=0;i--){
        if(father[y][i]!=father[x][i]){
            ans=min(ans,min(w[x][i],w[y][i]));
            y=father[y][i];
            x=father[x][i];
        }
    }
    ans=min(ans,min(w[x][0],w[y][0]));
    return ans;
}
int main(){
    fff();
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        int f,t,v;
        scanf("%d%d%d",&f,&t,&v);
        edge.push_back((Edge){f,t,v});
        edge.push_back((Edge){t,f,v});
    }
    kru();
    memset(visited,false,sizeof(visited));
    memset(deep,0,sizeof(deep));
    for (int i=1;i<=n;i++){
        if(!visited[i]){
            deep[i]=1;
            dfs(i);
            father[i][0]=i;
            w[i][0]=INF;
        }
    }
    sett();
    scanf("%d",&qury);
    for (int i=1;i<=qury;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/80968749