NOIP 2013 day1

tags:

  • 模拟
  • 快速幂
  • 逆序对
  • 树状数组
  • 归并排序
  • 最小生成树
  • lca
  • 倍增
    categories:
  • 信息学竞赛
  • 总结

tex live 2017.iso
转圈游戏
火柴排队
货车运输

转圈游戏

solution

  就是要求让一个人在一个圈上走一定步数, 问最后在哪里.例如走\(10^k\)次, 一次走\(\text{m}\)步, 初始时在\(\text{x}\), 圈长\(\text{n}\), 那么它最后的位置就是:
\[
x+m10^k\mod n
\]
那么直接用快速幂暴力求出这个式子的值.

code

#include<cstdio>
int n,m,k,x;

long long pow(int a,int b){
    long long ans=1ll,bas=a;
    while(b){
        if(b&1)ans*=bas;
        ans%=n;
        if(!bas)return ans;
        bas*=bas;bas%=n;
        if(!bas)return false;
        b>>=1;
    }
    return ans;
}

int main(){
    scanf("%d%d%d%d",&n,&m,&k,&x);
    long long ans=x,mm=pow(10,k);
    ans+=m*mm%n;
    ans%=n;
    printf("%lld\n",ans);
    return 0;
}

火柴排队

solution

  其实就是将其中一个通过交换两个相邻的元素使得变化后的序列与另一个序列最相近, 问操作次数.
  其实就是使得两个序列中最大的元素位置相同, 第二大的元素位置也相同, 第三大的元素位置相同......那么元素的具体大小其实根本无所谓, 只需要知道元素之间的大小关系, 所以可以先将两个序列离散化, 然后找到其中一个的元素在另一个中的位置, 记为序列\(\text{c}\), 经离散后的序列记为\(\text{a,b}\), 那么我们进行的交换操作, 实际上就是将\(\text{c}\)变为序列\(1,2,\cdots ,n\), 这样其实就是将\(\text{b}\)变成\(\text{a}\).

Code

#include<algorithm>
#include<cstdio>
#define N 100005
#define mod 99999997

int ans;
int n,u[N];
int a[N],b[N];
int e[N],f[N],g[N];

void mergesort(int l,int r){
    if(l==r)return;
    int mid,i,j,k;
    mid=(l+r)>>1;
    mergesort(l,mid);
    mergesort(mid+1,r);
    i=l,j=mid+1,k=l;
    while(i<=mid&j<=r)
        if(g[i]>=g[j])u[k++]=g[j++],ans+=(mid-i+1)%mod,ans%=mod;
        else u[k++]=g[i++];
    while(i<=mid)u[k++]=g[i++];
    while(j<=r)u[k++]=g[j++];
    for(i=l;i<=r;i++)g[i]=u[i];
}

struct Node{
    int val,pos;
    bool operator<(const Node& s)const{
        return val<s.val;
    }
}c[N],d[N];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)scanf("%d",&b[i]);
    for(int i=1;i<=n;++i)c[i]=(Node){a[i],i};
    for(int i=1;i<=n;++i)d[i]=(Node){b[i],i};
    std::sort(c+1,c+n+1);std::sort(d+1,d+n+1);
    for(int i=1;i<=n;++i)e[c[i].pos]=i;
    for(int i=1;i<=n;++i)f[d[i].pos]=i;
    for(int i=1;i<=n;++i)g[i]=c[f[i]].pos;
    mergesort(1,n);
    printf("%d",ans);
    return 0;
}

货车运输

  这个经典的题目不知道做了多少遍了, 这次做还是发现了一些问题.

Solution

  需要使得图上两点之间经过路径上边的最小值最大, 首先将原图变成最大生成树, 因为两点之间经过的最小边权最大的路径必然存在在最大生成树上. 记两点之间的路径的转折点为\(\text{lca}\)(最近公共祖先),在最大生成树上两点\(\text{u,v}\)之间路径上边的最小值就是\(\text{u}\)到\(\text{lca}\)路径上边的最小值或者是\(\text{u}\)到\(\text{lca}\)路径上边的最小值.

  • 需要用倍增来加速\(\text{lca}\)的求解, 也可以用树链剖分, 用\(f(u,i)\)表示从\(\text{u}\)向上第\(2^i\)个点是哪个点.递推公式为:
    \[
    f(u,i)=f(f(u,i-1),i-1)
    \]
  • 求\(树上两点之间路径的最小值\)可以使用\(\text{RMQ}\)算法, 用\(d(u,i)\)表示从$\text{u}以上\(2^i\)个点路径的最小值, 那么递推公式为:
    \[
    d(u,i)=\max{d(u,i-1),d(f(u,i-1),i-1)}
    \]

  \(f(u,0)\)和\(d(u,0)\)可以在dfs中直接得到, \(f(u,i)\)和\(d(u,i)\)需要递推来获得.

Code

#include<algorithm>
#include<cstdio>
#define N 500005
#define inf 0x3f3f3f3f
int n,m,q;
int fa[N];
int dep[N];
int f[N][20];
int d[N][20];

int min(int i,int j){
    return i>j?j:i;
}

int find(int s){
    if(fa[s]!=s)fa[s]=find(fa[s]);
    return fa[s];
}
struct E{
    int u,v,cost;
    void Init(){
        scanf("%d%d%d",&u,&v,&cost);
    }
    bool operator<(const E&s)const{
        return cost>s.cost;
    }
    void print(){
        printf("%d %d %d\n",u,v,cost);
    }
}edge[N];

int head[N],tot;
struct Edge{
    int v,c,nxt;
}e[N];

void AddEdge(int u,int v,int c){
    e[++tot]=(Edge){v,c,head[u]};head[u]=tot;
    e[++tot]=(Edge){u,c,head[v]};head[v]=tot;
}

void dfs(int x,int fath){
    f[x][0]=fath;dep[x]=dep[fath]+1;
    for(int i=head[x];i;i=e[i].nxt){
        if(e[i].v==fath)continue;
        d[e[i].v][0]=e[i].c;dfs(e[i].v,x);
    }
}

int Lca(int u,int v){
    int cha;
    if(dep[u]<dep[v])std::swap(u,v);
    for(int i=19;i>-1;--i)
        if(dep[f[u][i]]>=dep[v])
            u=f[u][i];
    for(int i=19;i>-1;--i)
        if(f[u][i]!=f[v][i])
            u=f[u][i],v=f[v][i];
    if(u!=v)return f[u][0];
    return u;
}

int Ans(int u,int v){
    if(u==v)return inf;
    int ans=inf;
    for(int i=19;i>-1;--i)
        if(dep[f[u][i]]>=dep[v])
            ans=min(d[u][i],ans),u=f[u][i];
    return ans;
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)edge[i].Init();
    for(int i=1;i<=n;++i)fa[i]=i;
    std::sort(edge+1,edge+m+1);int f1,f2,flag=0;
    for(int pos=1,f1,f2,i=1;i<n;++i){
        for(f1=find(edge[pos].u),f2=find(edge[pos].v);f1==f2;++pos,f1=find(edge[pos].u),f2=find(edge[pos].v))
                if(pos>m){flag=1;break;}
        if(flag)break;
        fa[f1]=f2;
        AddEdge(edge[pos].u,edge[pos].v,edge[pos].cost);
        //edge[pos].print();
    }
    dfs(1,0);
    for(int i=1;i<20;++i)
        for(int j=1;j<=n;++j)
            f[j][i]=f[f[j][i-1]][i-1];
    for(int i=1;i<20;++i)
        for(int j=1;j<=n;++j)
            d[j][i]=min(d[j][i-1],d[f[j][i-1]][i-1]);
    scanf("%d",&q);
    int u,v,lca;
    while(q--){
        scanf("%d%d",&u,&v);
        lca=Lca(u,v);
        if(find(u)!=find(v))printf("-1\n");
        else printf("%d\n",min(Ans(u,lca),Ans(v,lca)));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/qdscwyy/p/8728113.html

时间: 2024-10-13 18:33:12

NOIP 2013 day1的相关文章

NOIp 2013 Day1 解题报告

NOIp 2013 Day1 解题报告 1.   转圈游戏 不难看出答案就是(x+m*10k) mod n 用快速幂算法,复杂度O(log2k) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable// 9 int n,m,x,

NOIP 2013 火车运输【Kruskal + 树链剖分】

NOIP 2013 火车运输[树链剖分] 树链剖分 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入描述 Input Description 第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路.接下来 m 行每行 3 个整数 x.y.z,每两个整数之间用一个空格隔开,表示

NOIp 2013 Day2 解题报告

NOIp 2013 Day2 解题报告 1.   积木大赛 每次只要选取连续最大的一段区间即可. 继续归纳可得,答案为∑i=1nmax{0,hi-hi-1} 复杂度O(N) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable/

NOIP 2013 提高组 Day1

题目:http://wenku.baidu.com/link?url=WgP-2UcEJy53ShbZC3gQVHZTXrHbgg14COe3HE9ybEfI82sr8nMvc-FobNBS9WwQxCEiCtb3TdETK-ZD3i3F1lMOqq3-J1cfkTrfhrbXLJe 1.转圈游戏 [快速幂] 注意:二分时记得保存值,不可重复运算: 1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #

【CodeVS 3290】【NOIP 2013】华容道

http://codevs.cn/problem/3290/ 据说2013年的noip非常难,但Purpleslz学长还是AK了.能A掉这道题真心orz. 设状态$(i,j,k)$表示目标棋子在$(i,j)$这个位置,空格在紧贴着目标棋子的$k$方向,$0≤k<4$. 因为目标棋子要移动,空格肯定在它旁边.往空格的方向走一步,空格便出现在它另一边.对于这两个状态连边,边权为1. 为了使目标棋子向某一方向移动,需要目标棋子不动,空格从紧贴着目标棋子的某一方向移动到紧贴着目标棋子的另一个方向.对于固

Noip 2016 Day1 题解

老师让我们刷历年真题, 然后漫不经心的说了一句:“你们就先做做noip2016 day1 吧” ...... 我还能说什么,,,,,老师你这是明摆着伤害我们啊2333333333 预计分数:100+25+24 实际分数:100+25+12 T1:玩具谜题 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业. 有一天, 这些玩具小人把小南的眼镜藏了起来. 小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外.如下图: 这时singer告诉小南一个谜題: “眼镜藏在我左数第3个玩具小

noip 2015 day1

T1 神奇的幻方 题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. 之后,按如下方式从小到大依次填写每个数K(K=2,3,…,N*N): 1.若(K−1)在第一行但不在最后一列,则将K填在最后一行,(K−1)所在列的右一列: 2.若(K−1)在最后一列但不在第一行,则将K填在第一列,(K−1)所在行的上一行: 3.若(K−1)在第一行最后一列,则

洛谷P1328==codevs3716 生活大爆炸版石头剪刀布[NOIP 2014 day1 T1]

P1328 生活大爆炸版石头剪刀布 1.8K通过 2.6K提交 题目提供者2014白永忻 标签模拟NOIp提高组2014 难度普及- 提交该题 讨论 题解 记录 最新讨论 Who can help me(+﹏+)~ hehe 我去 数据错误 题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一样,则不分胜负.在<生活大爆炸>第二季第8 集中出现了一种石头剪刀布的升级版游戏. 升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势: 斯波克:<星际迷航

NOIP 2012 Day1

tags: NOIP 模拟 倍增 高精 Python categories: 信息学竞赛 总结 Luogu P1079 Vigenère 密码 Solution 表示并不是很懂其他人发的题解. 我是这么想的, 既然是题目要求用密文转明文而且转换规则一定的, 所以就可以用明文转密文的逆过程来完成. 首先要搞明白明文是怎么变成密文的, 通过这个表可以观察到, 如果明文的一个字符是 ch1 ,密钥为 ch2 , 那么密文 ch3 对应的就是 ch1 在字母表中偏移 |ch2| 位, 例如 \(\tex