NOIP2013 Day1

1.转圈游戏

https://www.luogu.org/problem/show?pid=1965

这道题失误极大,把freopen注释掉了,导致第一题暴0.

注意:在考试时一定要留下最后的时间检查格式!!!

由于此题中k的值很大,所以暴力只能过80%数据,需要用到快速幂。

#include <cstdio>
long long n,m,k,x,ans,t;
int main()
{
    freopen("circle.in","r",stdin);
    freopen("circle.out","w",stdout); 
    scanf("%lld%lld%lld%lld",&n,&m,&k,&x);
    ans=1; t=10;
    while(k)
    {
        if(k%2==1)ans=(ans*t)%n;
        k/=2;
        t=((t%n)*(t%n))%n;
    }
    printf("%d",(x+(m*ans)%n)%n);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

2.火柴排队

https://www.luogu.org/problem/show?pid=1966

这道题可用树状数组||逆序对||归并排序完成

献上树状数组代码:

#include<cstdio>
#include<algorithm>
using namespace std;
struct MyStruct
{
    int data;
    int loc;
}a[100010],b[100010];
int e[100010], n, c[100010];
int inline readint()
{
    int x = 0;
    char c = getchar();
    while (c<'0' || c>'9') c = getchar();
    while (c >= '0'&&c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x;
}
int lowbit(int x)
{
    return x&-x;//树状数组实现 
}
void add(int x,int t)
{
    while (x <= n)
    {
        e[x] += t;
        e[x] %=99999997;
        x += lowbit(x);//每次往后加,可以改变后面对应的和 
    }
}
int sum(int x)
{
    int s = 0;
    while(x)
    {
        s += e[x];
        s %= 99999997;
        x -= lowbit(x);//得到所求的和 
    }
    return s;
}
bool cmp(MyStruct x, MyStruct y)
{
    return x.data < y.data;
}
int main()
{
    n = readint();
    for (int i = 1; i <= n; i++)
    {
        a[i].data = readint();
        a[i].loc = i;//记录位置 
    }
    for (int i = 1; i <= n; i++)
    {
        b[i].data = readint();
        b[i].loc = i;
    }
    sort(a + 1, a + n + 1, cmp);
    sort(b + 1, b + n + 1, cmp);
    for (int i = 1; i <= n; i++)
    {
        c[a[i].loc] = b[i].loc;//离散优化 
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        add(c[i], 1);//离散优化后大小就是正确顺序的位置 
        ans += i - sum(c[i]);//当前位置,减去之前比他大的数的个数  
        ans %= maxm;
    }
    printf("%d", ans);
    return 0;
}

3.货车运输

本题用LCA+最大生成树完成。。。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define MAXN 10005
using namespace std;
int n,m;
struct T
{
    int v;
    int w;
    int next;
}edge[100005];
 
struct P
{
    int u;
    int v;
    int w;
}a[MAXN*5];
bool cmp(P x,P y)
{
    return x.w > y.w;
}
 
int head[MAXN],cnt;
int f[MAXN];
int find(int x)//并查集,判断是否在同一个集合内
{
    if(f[x] == x) return f[x];
    else return f[x] = find(f[x]);
}
void Add_edge(int u,int v,int w)//树连边
{
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
void Union(int u,int v)//联通块
{
    int x = find(u);
    int y = find(v);
    if(x != y) f[x] = y;
}
void kruskal()//最大生成树
{
    for(int i = 0; i <= MAXN; i++)
        f[i] = i;
    for(int i = 1; i <= m; i++)
    {
        int u = a[i].u,v = a[i].v;
        if(find(u) != find(v))
        {
            Union(u,v);
            Add_edge(u,v,a[i].w);
            Add_edge(v,u,a[i].w);
        }
    }
}
 
int up[MAXN][25],g[MAXN][25],h[MAXN];//up[i][j]表示i的第2^j个祖先,g[i][j]表示i到i的第2^j个祖先路径上的最小权值,h[i]表示i在树中深度
bool vis[MAXN];
void build_tree(int u)
{
    vis[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!vis[v])
        {
            g[v][0] = edge[i].w; 
            up[v][0] = u;
            h[v] = h[u]+1;
            build_tree(v);
        }
    }
}
//把较深的一个点往上提,并记录他到祖先边权最小值,用他的一个祖先代替他
int move(int &u,int H)
{
    int res = 123546789;
    for(int i =20; i >= 0; i--)
    {
        if(h[up[u][i]] >= H)
        {
            res = min(res,g[u][i]);
            u = up[u][i];
        }
    }
    return res;
}
int query(int u,int v)//自认为是最难的地方
{
    if(find(u) != find(v)) return -1;
    int res = 123456789;
    if(h[u] != h[v]) res = h[u] > h[v]?move(u,h[v]):move(v,h[u]);
    if(u == v) return res;
    for(int i = 20; i >= 0; i--)//倍增的同时记录最小值,两个点越来越逼近公共祖先
    {
        if(up[u][i] != up[v][i])
        {
            res = min(res,min(g[u][i],g[v][i]));
            u = up[u][i];
            v = up[v][i];
        }
    }
    res = min(res,min(g[u][0],g[v][0]));//实际上到了这一步up[x][0] == up[y][0]因为它们的已经在同一棵子树里面
    //printf("up[u][0]: %d
",up[u][0]);
    //printf("up[v][0]: %d
",up[v][0]);
    return res;
}
 
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i++)
        scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
    sort(a+1,a+m+1,cmp);
    kruskal();
     
    for(int i = 1; i <= n; i++)//构建森林,并且初始化h,up,g
    {
        if(!vis[i])
        {
            h[i] = 0;
            build_tree(i);
            g[i][0] = 123456789;
            up[i][0] = i;
        }
    }
    for(int i = 1; i <= 20; i++)//预处理up和g,i大了也没有什么影响
    {
        for(int j = 1; j <= n; j++)
        {
            up[j][i] = up[up[j][i-1]][i-1];
            g[j][i] = min(g[j][i-1],g[up[j][i-1]][i-1]);
        }
    }
     
    int q;
    scanf("%d",&q);
    for(int i = 1; i <= q; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d
",query(x,y));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wisdom-jie/p/7738164.html