noip2013

来源:互联网 发布:s7 200编程电缆 编辑:程序博客网 时间:2024/06/05 06:48

day1
t1
转圈游戏(circle)
裸地快速幂。。

#include<cstdio>using namespace std;int n,m,x,k;int fastpow(int a,int k){    int ans=1;    while(k){        if(k&1) ans=(1ll*ans*a)%n;        a=(1ll*a*a)%n;        k>>=1;    }    return ans;}int main() {    scanf("%d%d%d%d",&n,&m,&k,&x);    printf("%d",(x+1ll*m*fastpow(10,k)%n)%n);    return 0;}

t2
火柴排队
这个题可以贪心,最小价值一定是大小位置对应的,所以用下面的匹配上面的就行了
因为火柴棒数目有点大,需要离散化一下,因为火柴的长度不一。
然后可以对第一个串遍一个号,对应到第二个串里,就变成了两两交换排序问题
再一次转换为逆序对问题,就a了

#include<cstdio>#include<algorithm>using namespace std;#define ll long longconst int M=101001;const int P=99999997;struct st{ll f,s;};int n;st a[M];ll t[M],w[M],sum[M],ans,ff[M],tmp[M],tmp2[M];void add(ll x){    for(;x;x-=x&(-x))            sum[x]++;}ll ask(ll x){    ll tot=0;    for(;x<=n;x+=x&(-x))    tot=(tot+sum[x])%P;    return tot;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%lld",&a[i].f),t[i]=a[i].f;    for(int i=1;i<=n;i++) scanf("%lld",&a[i].s),w[i]=a[i].s;    sort(t+1,t+n+1);sort(w+1,w+n+1);    for(int i=1;i<=n;i++){        a[i].f=lower_bound(t+1,t+n+1,a[i].f)-t;        a[i].s=lower_bound(w+1,w+n+1,a[i].s)-w;    }    for(int i=1;i<=n;i++) ff[a[i].f]=i;    for(int i=1;i<=n;i++){        add(ff[a[i].s]);        ans=(ans+ask(ff[a[i].s]+1))%P;    }    printf("%lld",ans%P);    return 0;}

t3
货车运输
先找出最大生成森林,再找倍增找lca,找最小值
之前写了个tarjan错了。。。

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define ll long longconst int M=21000,INF=1e9+3;int fa[M],vis[M],tote,heade[M],nexe[M*2],toe[M*2],cose[M*2],tot,head[M],nex[M*2],to[M*2],cos[M*2],f[M],dep[M];int ff[M],zx[M],cnt,dx[M],dy[M],up[M][20],g[M][20];int n,m,q;struct st{    int x,y,z;}a[M]; bool cmp(const st a,const st b){    return a.z>b.z;}void add(int x,int y,int z){    nex[++tot]=head[x];    to[tot]=y;    cos[tot]=z;    head[x]=tot;}void adde(int x,int y){    nexe[++tote]=heade[x];    toe[tote]=y;    heade[x]=tote;}int find(int x){    return f[x]==x?x:f[x]=find(f[x]);}void K(){    int j=1,num=n;        while(j<=m)        {            int fx=find(a[j].x);            int fy=find(a[j].y);            if(fx==fy) {                j++;continue;            }            f[fx]=fy;            add(a[j].x,a[j].y,a[j].z);            add(a[j].y,a[j].x,a[j].z);            j++;            num--;            if(num==1)             break;        }}void dfs(int x){   vis[x]=1;       for(int i=head[x];i;i=nex[i]){        int tmp=to[i];        if(!vis[tmp]){            up[tmp][0]=x;            g[tmp][0]=cos[i];            dep[tmp]=dep[x]+1;             dfs(tmp);         }    }} int lca(int x,int y)  {      if(x==y) return 0;    int fx=find(x);    int fy=find(y);    if(fx!=fy) return -1;    int minx=INF,miny=INF;    if(dep[x]<dep[y]) swap(x,y);    for(int i=18;i>=0;i--)    {        if(dep[up[x][i]]>=dep[y]&&up[x][i])        {            minx=min(minx,g[x][i]);            x=up[x][i];        }    }    if(x==y) return minx;    for(int i=18;i>=0;i--)    {        if(up[x][i]!=up[y][i])        {            minx=min(minx,g[x][i]);            miny=min(miny,g[y][i]);            x=up[x][i];            y=up[y][i];        }    }    minx=min(minx,g[x][0]);    miny=min(miny,g[y][0]);    return min(minx,miny);}  int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)    scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);    for(int i=1;i<=n;i++)f[i]=i;    sort(a+1,a+m+1,cmp);    K();   memset(g,127,sizeof g);        scanf("%d",&q);        for(int i=1;i<=q;i++){        scanf("%d%d",&dx[i],&dy[i]);        adde(dx[i],dy[i]);        adde(dy[i],dx[i]);    }    for(int i=1;i<=n;i++)     if(!vis[i])        dfs(i);    for(int j=1;(1<<j)<=n;j++)      for(int i=1;i<=n;i++)        if(up[up[i][j-1]][j-1]!=0){          up[i][j]=up[up[i][j-1]][j-1];          g[i][j]=min(g[i][j-1],g[up[i][j-1]][j-1]);    }    //for(int i=1;i<=2*q;i++) printf("%d ",lca[i]);    for(int i=1;i<=q;i++){        if(find(dx[i])!=find(dy[i])) printf("-1\n");else        printf("%d\n",lca(dx[i],dy[i]));    }} 

day2
t1
这个题正解是单调栈,因为山包你把它砍掉就行,只有上升可以增加高度,下降就可以直接去掉
这就是个单调栈,然后更新答案,用山顶减山脚
我是用了个优先队列+链表,也搞对了,我是每一次砍掉最高的山,然后把最高的山去掉,你可以看做和旁边的山合并成一个山了,这个可以用优先队列,然后记录个左右山的序号,用看得比较小的,更新答案,删山时用链表搞一下

#include<cstdio>#include<iostream>#include<queue>using namespace std;const int M=110000;struct st{    int x,t;    inline int operator <(const st &a)const{         return a.x<x;     }};int n,a[M];int cnt=1,f=1,h[M],minn[M];int ans,l[M],r[M];priority_queue < st > q;int t=0;int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%d",&a[i]);        if(a[t]!=a[i])        {            st tmp;            tmp.x=-a[i];            tmp.t=i;            q.push(tmp);            l[i]=t,r[t]=i;            t=i;        }    }    while(!q.empty()){        st tmp=q.top();        //printf("%d ",-tmp.x);        q.pop();        l[r[tmp.t]]=l[tmp.t];        r[l[tmp.t]]=r[tmp.t];        ans+=min(-tmp.x-a[l[tmp.t]],-tmp.x-a[r[tmp.t]]);    }    printf("%d",ans);}

t2
花匠
想一个问题,最后选的花一定是波浪线,一定包括所有峰(比左右都大得),所以结果就是峰的个数乘2
在特判一下两端是否是峰

#include<cstdio>#include<iostream>using namespace std;int n,a[1999999],tot;int s,f;int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) {        int x;scanf("%d",&x);        if(a[tot]!=x) a[++tot]=x;    }    for(int i=1;i<=tot;i++) {        if(a[i]>a[i-1]&&a[i]>a[i+1]) s++;    }    s=s*2;    if(a[1]<a[2]) s++;    if(a[tot]>a[tot-1]) s--;    printf("%d",s);}

t3
华容道