【NOIP2013TG】solution

链接:https://www.luogu.org/problem/lists?name=&orderitem=pid&tag=83%2C30

D1T1:转圈游戏(circle)

题意:看题目。。

解题思路:快速幂求\( (10^k m) mod n \)即可。时间效率\( O (\lg k) \)

#include<stdio.h>
#define ll long long
#define For(i,a,b) for(int i=a; i<=b; i++)
#define Ford(i,a,b) for(int i=a; i>=b; i--)
#define File(fn) freopen(fn".in","r",stdin);freopen(fn".out","w",stdout);
int n,m,k,x;
inline int in(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void print(int x,char ch){
    if (!x){
        putchar('0');
        putchar(ch);
        return;
    }
    if (x<0){
        putchar('-');
        x*=-1;
    }
    char num[10];
    short cnt=0;
    while(x) num[++cnt]=x%10+'0',x/=10;
    while(cnt) putchar(num[cnt--]);
    putchar(ch);
}
inline ll ksm(int a,int k){
    if (!k) return 1;
    if (!(k^1)) return a;
    ll t=ksm(a,k>>1)%n;
    t*=t;t%=n;
    if (k&1) t*=a;
    t%=n;
    return t;
}
int main(){
    n=in(),m=in(),k=in(),x=in();
    print((ksm(10,k)*m%n+x)%n,'\n');
}

D1T2:火柴排队(match)

题意:将一个数组中元素进行几次交换后使得这个数组的大小顺序与另一个数组相同。

解题思路:离散后排序求逆序对数。时间效率\( O(n \lg n) \).

#include<stdio.h>
#include<algorithm>
#define ll long long
#define For(i,a,b) for(int i=a; i<=b; i++)
#define Ford(i,a,b) for(int i=a; i>=b; i--)
#define File(fn) freopen(fn".in","r",stdin);freopen(fn".out","w",stdout);
#define mod 99999997
using namespace std;
struct zxy{
    int num,no;
}a[100001],b[100001];
int n,ans=0,c[100001],BIT[100001];
inline int in(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void print(int x,char ch){
    if (!x){
        putchar('0');
        putchar(ch);
        return;
    }
    if (x<0){
        putchar('-');
        x*=-1;
    }
    char num[10];
    short cnt=0;
    while(x) num[++cnt]=x%10+'0',x/=10;
    while(cnt) putchar(num[cnt--]);
    putchar(ch);
}
bool cmp(zxy a,zxy b){
    return a.num<b.num;
}
void init(){
    n=in();
    For(i,1,n) a[i].num=in(),a[i].no=i;
    For(i,1,n) b[i].num=in(),b[i].no=i;
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    For(i,1,n) c[b[i].no]=a[i].no;
}
inline int lowbit(int k){
    return k&(-k);
}
inline void update(int x,int ad){
    while(x<=n){
        BIT[x]+=ad;
        x+=lowbit(x);
    }
}
inline int query(int x){
    int sum=0;
    while(x){
        sum+=BIT[x];
        x-=lowbit(x);
    }
    return sum;
}
void solve(){
    For(i,1,n){
        update(c[i],1);
        ans+=(i-query(c[i]));
        ans%=mod;
    }
    print(ans,'\n');
}
int main(){
    init();
    solve();
    return 0;
}

D1T3:货车运输(truck)

题意:给你一张图,问你从一个点到另一个点的路径上的最大的路径上最小的权值是多少,如果不能联通输出-1.

解题思路:首先为了方便处理,我们将所有点向0号点连一条权值为-1的边,这样保证所有点可以联通的同时会输出答案-1.接下来,我们考虑题意要求的使得“最小的尽可能”大的条件,发现可以使用最大生成树实现,接下来考虑查询两点间的树上路径中权值最小的边,使用LCA即可作答,这里可以使用所有的LCA算法,我使用了理解与实现都较为容易倍增求LCA,时间效率为\( O(m \lg m+q \lg n) \)。

附优化了各种常数的代码(貌似还可以继续优化)。

#include<stdio.h>
#include<algorithm>
#define INF 0x7fffffff
using namespace std;
struct zxy{
    int x,y,l;
}edge[150001];
struct tree{
    int next,to,v;
}tr[20005];
int father[10001],fa[10001][15],n,m,q,cnt=0,used[10005],deep[10005],head[10005],dis[10001][15];
inline int in(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline int find(int x){
    return father[x]=father[x]==x?x:find(father[x]);
}
bool cmp(zxy a,zxy b){
    return a.l>b.l;
}
inline void ins(int x,int y,int l){
    edge[++cnt].x=x,edge[cnt].y=y,edge[cnt].l=l;
}
inline void ins2(int x,int y,int l){
    tr[++cnt].to=y,tr[cnt].next=head[x],head[x]=cnt,tr[cnt].v=l;
}
void init(){
    n=in(),m=in();
    int x,y,l;
    for (int i=1; i<=m; ++i){
        x=in(),y=in(),l=in();
        ins(x,y,l);ins(y,x,l);
    }
}
bool mst(){
    for(register int i=1; i<=n; ++i)
        father[i]=i,ins(0,i,-1),ins(i,0,-1);
    sort(edge+1,edge+cnt+1,cmp);
    int k=0;
    for(register int i=1; i<=cnt; ++i)
        if (find(edge[i].x)!=find(edge[i].y)){
            father[find(father[edge[i].x])]=find(edge[i].y);
            used[++k]=i;        
        }
    cnt=0;
    for(register int i=1; i<=k; ++i) 
        ins2(edge[used[i]].x,edge[used[i]].y,edge[used[i]].l),ins2(edge[used[i]].y,edge[used[i]].x,edge[used[i]].l);
    return !(k^n);
}
inline void dfs(int x,int d,int f){
    deep[x]=d;fa[x][0]=f;
    for (register int i=head[x]; i; i=tr[i].next)
        if (tr[i].to!=f) dis[tr[i].to][0]=tr[i].v,dfs(tr[i].to,d+1,x);
}
void init_LCA(){
    for(int j=1; j<=14; ++j)    
        for(register int i=0; i<=n; ++i)
            fa[i][j]=fa[fa[i][j-1]][j-1],dis[i][j]=min(dis[i][j-1],dis[fa[i][j-1]][j-1]);
}
inline int query(int x,int y){
    if (deep[x]<deep[y]){int t=x;x=y,y=t;}
    int mi=INF,d=deep[x]-deep[y];
    if (d) for(register int i=0; d&&i<=14; ++i,d>>=1) if (d&1) mi=min(mi,dis[x][i]),x=fa[x][i];
    if (!(x^y)) return mi;
    for(register int i=14; i>=0; --i) if (fa[x][i]!=fa[y][i]) mi=min(mi,min(dis[x][i],dis[y][i])),x=fa[x][i],y=fa[y][i];
    mi=min(mi,min(dis[x][0],dis[y][0]));
    return mi;
}
void lca(){
    q=in();
    dfs(0,0,0);init_LCA();
    for(register int i=1; i<=q; ++i){
        register int x=in(),y=in();
        printf("%d\n",query(x,y));
    }
}
int main(){
    init();
    mst();
    lca();
    return 0;
}

 

D2T1:积木大赛(block)

题意:看题目。。

解题思路:暴力模拟,具体见代码。时间效率\(O(n)\)。

#include<stdio.h>
#define ll long long
#define For(i,a,b) for(int i=a; i<=b; i++)
#define Ford(i,a,b) for(int i=a; i>=b; i--)
#define File(fn) freopen(fn".in","r",stdin);freopen(fn".out","w",stdout);
int a[100001],n,h,t,ans;
inline int in(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void print(int x,char ch){
    if (!x){
        putchar('0');
        putchar(ch);
        return;
    }
    if (x<0){
        putchar('-');
        x*=-1;
    }
    char num[10];short cnt=0;
    while(x) num[++cnt]=x%10+'0',x/=10;
    while(cnt) putchar(num[cnt--]);
    putchar(ch);
}
int main(){
    n=in();
    For(i,1,n) a[i]=in();
    h=0;
    For(i,1,n)
        if (a[i]<a[i-1]) ans+=a[i-1]-a[h],h=i;
    ans+=a[n]-a[h];
    print(ans,'\n');
}

 D2T2:花匠(flower)

题意:看题目啊。。。

解题思路:显然题目就是叫你找一个最长的连续上升子序列/下降子序列,所以乱搞一下就好了。时间效率\(O(n)\).

#include<stdio.h>
#define ll long long
#define For(i,a,b) for(int i=a; i<=b; i++)
#define Ford(i,a,b) for(int i=a; i>=b; i--)
#define File(fn) freopen(fn".in","r",stdin);freopen(fn".out","w",stdout);
int a[100001],ans=1,flag=-1,n;
inline int in(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void print(int x,char ch){
    if (!x){
        putchar('0');
        putchar(ch);
        return;
    }
    if (x<0){
        putchar('-');
        x*=-1;
    }
    char num[10];
    short cnt=0;
    while(x) num[++cnt]=x%10+'0',x/=10;
    while(cnt) putchar(num[cnt--]);
    putchar(ch);
}
int main(){
    n=in();
    For(i,1,n) a[i]=in();
    For(i,2,n){
        if (a[i]>a[i-1]&&(flag-1)) ans++,flag=1;
        if (a[i]<a[i-1]&&flag) ans++,flag=0;
    }
    print(ans,'\n');
}

 本文由Melacau编写,Melacau代表M星向您问好,如果您不是在我的博客http://www.cnblogs.com/Melacau上看到本文,请您向我联系,email:13960948839@163.com.

posted @ 2017-03-14 11:37  Melacau  阅读(228)  评论(0编辑  收藏  举报