[竞赛][比赛题解]Noip2016双日游

11.18


联赛前一天,作为强省的弱OIer,第一次参加Noip提高组还是挺紧张的。于是在训练的小黑屋了浪了一整天……


11.19


Day1
因为本土作战,而且家离学校近,所以睡到7:40才起床。
T1 模拟暴力,考试的时候竟然没注意到mogician……
T2 由于做了前几年的联赛,感觉前两道题应该挺水的,结果愣是看了一个多小时想不出正解……只好打80分的暴力。
T3 正解打挂没时间,搜索暴力……


11.20


Day2
T1 O(N2) 预处理后 O(1) 输出
T2 吸取第一天的教训,第二题直接STL堆暴力
T3 打完暴搜后还有一个多小时,开始着手优化搜索……在想记忆化的时候猛然想到可以状压DP……结果被卡精度,又没预处理… O(T2NN3)


11.28


听说零点出成绩,于是兴致冲冲地等到了0:00,结果CCF没上班,网页只有一个空壳…..
下午跑到机房,果然出成绩了……

这里写图片描述

12.8


分数线330……


题解

Day1


T1 toy

直接暴力模拟。 O(N)

#include 
#include 
#include 
#include 
#define N 100010

using namespace std;

int n,f[N],m,noww;
char namE[N][20];

void reaD(int &x){
    char Ch=getchar();x=0;
    for(;Ch>'9'||Ch<'0';Ch=getchar());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}

void reaDs(char a[]){
    char Ch=getchar();int len=0;
    for(;Ch>'z'||Ch<'a';Ch=getchar());
    for(;Ch>='a'&&Ch<='z';a[++len]=Ch,Ch=getchar());
    a[++len]=0;
}

void M(int &noww){
    if(noww<=0) noww+=n;
    if(noww>n) noww-=n;
}

int main(){
    reaD(n);reaD(m);
    for(int i=1;i<=n;i++)
        reaD(f[i]),reaDs(namE[i]);
    noww=1;
    for(int i=1,LoR,Lo;i<=m;i++){
        reaD(LoR);reaD(Lo);
        if(f[noww]^LoR) noww+=Lo;
        else noww-=Lo;
        M(noww);
    }
    for(int i=1;namE[noww][i]!=0;i++) putchar(namE[noww][i]);
    return 0;
}    

T2 running

根据提供的数据信息可拿80……

对于一个从u到v的人,令 f u , v 的Lca, d(x) x 的深度, F(x) x 的父节点

可以先考虑所有人都从根节点出发所有人都跑到根节点的做法:

  • 对于一个 T 时刻从 x 节点跑到根节点的人,如果路径上的观察员 i 满足 T+d(x)d(i)=w(i) ,即 T+d[x]=w[i]+d[i] , 那么观察员 i 可以观察到这个人。

  • 对于一个 T 时刻从根节点跑到 x 节点的人,如果路径上的观察员 i 满足 T+d(i)=w(i) ,即 T=w(i)d(i) ,那么观察员 i 就可以观察到这个人

所以我们可以把一个从 u v 的人分解:

  • 1个在0时刻从u跑到根节点的人
  • 1个 d(u)2d(f) 时刻从根节点跑到v的人

但是这样的话 F(f) -根节点这条路径上的观察员也会观察到这个人,所以可以增加两个玩家:

  • -1个 d(u)d(F(f)) 时刻从 F(f) 跑到根节点的人
  • -1个 d(u)2d(f) 时刻从根节点跑到 v 的人

然后按照所有人从根节点出发所有人跑到根节点的方法做。

那么复杂度就集中在求LCA的过程,用树剖或倍增

O(NlogN+N+M)

#include 
#define N 300010

int En,n,m;
int w[N],G[N],Ans[N],F[N],S[N],L[N],GG[N],tt;
int Top[N],ws[N],Dept[N],B[N],R[N],mi[N],GG1[N];

struct timble{
    int t,nx,w;
}Tb[N<<3];

struct edgee{
    int t,nx;
}E[N<<1];

inline void reaD(int &x){
    char Ch=getchar();x=0;
    for(;Ch>'9'||Ch<'0';Ch=getchar());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}

inline void InserT(int x,int y){
    E[++En].t=y;
    E[En].nx=G[x];
    G[x]=En;
}

inline void clf(){fclose(stdin);fclose(stdout);}

void D1(int x,int f,int d){
    F[x]=f;S[x]=1;
    for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f){
        D1(E[i].t,x,d+1);
        if(S[E[i].t]>S[ws[x]]) ws[x]=E[i].t;
        S[x]+=S[E[i].t];
    }
}

void D2(int x,int dep,int tp){
    Dept[x]=dep;Top[x]=tp;
    if(ws[x]) D2(ws[x],dep+1,tp);
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=F[x]&&E[i].t!=ws[x]) D2(E[i].t,dep+1,E[i].t);
}

inline void reaDedge(){
    for(int i=1,x,y;ifor(int i=1;i<=n;i++) reaD(w[i]);
    D1(1,0,1);D2(1,1,1);
}

inline int Lca(int x,int y){
    int tp1,tp2;
    while(Top[x]!=Top[y])
        if(Dept[Top[x]]else x=F[Top[x]];
    if(Dept[x]return x;
    return y;
}

inline void InsertT2(int x,int t,int w){
    Tb[++tt].nx=GG[x];
    GG[x]=tt;
    Tb[tt].t=t;
    Tb[tt].w=w;
}

inline void InsertT1(int x,int t,int w){
    Tb[++tt].nx=GG1[x];
    GG1[x]=tt;
    Tb[tt].t=t;
    Tb[tt].w=w;
}

void S1(int x,int f){
    int r=B[Dept[x]+w[x]];
    for(int i=GG1[x];i;i=Tb[i].nx)
        B[Tb[i].t]+=Tb[i].w;
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f) S1(E[i].t,x);
    Ans[x]+=B[Dept[x]+w[x]]-r;
}

int S2(int x,int f){
    int r;
    if(w[x]-Dept[x]>=0) r=R[w[x]-Dept[x]]; else r=mi[Dept[x]-w[x]];
    for(int i=GG[x];i;i=Tb[i].nx) 
        if(Tb[i].t>=0)R[Tb[i].t]+=Tb[i].w; else mi[-Tb[i].t]+=Tb[i].w;
    for(int i=G[x];i;i=E[i].nx)
        if(E[i].t!=f) S2(E[i].t,x);
    if(w[x]-Dept[x]>=0) Ans[x]+=R[w[x]-Dept[x]]-r;
    else Ans[x]+=mi[Dept[x]-w[x]]-r;
}

int ww[20],wt;

inline void Pt(int x){
    if(!x){putchar('0');return;}
    while(x)ww[++wt]=x%10,x/=10;
    for(;wt;wt--)putchar(ww[wt]+'0');
}

int main(){
    reaD(n);reaD(m);
    reaDedge();
    for(int i=1,u,v,f;i<=m;i++){
        reaD(u);reaD(v);
        f=Lca(u,v);
        InsertT1(u,Dept[u],1);
        InsertT1(F[f],Dept[u],-1);
        InsertT2(f,Dept[u]-Dept[f]*2,-1);
        InsertT2(v,Dept[u]-2*Dept[f],1);
    }
    S1(1,0);S2(1,0);
    for(int i=1;iputchar(' ');
    return Pt(Ans[n]),0;
}

T3 classroom

概率DP
f[i][j][k] 表示前 i 节课,申请了换 j 节课, k=1 表示第i节课申请了换课, k=0 表示没有。

g(i,j) 表示 i 教室与 j 教室的最短距离

那么对于第 i 节课,如果不申请换课:

  • 若第 i1 节课没有申请换课, f[i][j][0]=f[i1][j][0]+g(c[i1],c[i])
  • 若第 i1 节课申请了换课,那么
    – 有 k[i1] 的概率申请成功,要消耗 g(d[i1],c[i]) 的体力
    – 有 (1k[i1]) 的概率申请失败,要消耗 g(c[i1],c[i]) 的体力
    所以期望消耗 k[i1]g(d[i1],c[i])+(1k[i1])g(c[i1],c[i]) 的体力

如果申请换课,同意按照上面的方法分类,只不过要同时考虑第 i 节课和第 i1 节课的申请成功的情况。

计算两个教室间的最短路程,因为教室数比较小,可以floyd

O(v3+nm)

#include 
#include 
#include 
#include 
#include 
#include 
#define N 2010
#define M 310

using namespace std;

typedef long long ll;

int n,m,v,e,C[N],D[N],w[N];
double K[N],Ans,f[2][N][2];
ll d[M][M];

inline void reaD(int &x){
    char Ch=getchar();x=0;
    for(;Ch>'9'||Ch<'0';Ch=getchar());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}

inline int min(const int &a,const int &b){
    return ainline double min(double a,double b,double c){
    return min(min(a,b),c);
}

int main(){
    reaD(n);reaD(m);reaD(v);reaD(e);
    for(int i=1;i<=v;i++){
        for(int j=1;j<=v;j++) d[i][j]=1<<30;
        d[i][i]=0;
    }
    for(int i=1;i<=n;i++) reaD(C[i]);
    for(int i=1;i<=n;i++) reaD(D[i]);
    for(int i=1;i<=n;i++){
        scanf("%lf",&K[i]);
    }
    for(int i=1,x,y,z;i<=e;i++){
        reaD(x);reaD(y);reaD(z);
        d[x][y]=d[y][x]=min(d[x][y],z);
    }
    for(int k=1;k<=v;k++)
        for(int i=1;i<=v;i++)
            for(int j=1;j<=v;j++)
                if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];
    memset(f,0x7F,sizeof(f));
    f[1][1][1]=f[1][0][0]=0;
    for(int i=2;i<=n;i++)
        for(int j=0;j<=i&&j<=m;j++){
            f[i&1][j][0]=min(f[i&1^1][j][0]+d[C[i-1]][C[i]],f[i&1^1][j][1]+d[D[i-1]][C[i]]*K[i-1]+d[C[i-1]][C[i]]*(1-K[i-1]));
            if(j) f[i&1][j][1]=min(f[i&1^1][j-1][0]+d[C[i-1]][D[i]]*K[i]+d[C[i-1]][C[i]]*(1-K[i]),
                                 f[i&1^1][j-1][1]+d[D[i-1]][D[i]]*K[i]*K[i-1]+d[D[i-1]][C[i]]*K[i-1]*(1-K[i])+d[C[i-1]][D[i]]*K[i]*(1-K[i-1])+d[C[i-1]][C[i]]*(1-K[i])*(1-K[i-1]));
        }
    Ans=1<<30;
    for(int i=0;i<=m;i++) Ans=min(Ans,f[n&1][i][0],f[n&1][i][1]);
    printf("%.2lf",Ans);
    return 0;
}

Day2


T1 problem

根据 C(i,j)=C(i1,j1)+C(i,j1) 预处理后输出

但是因为 C(i,j) 会很大,所已预处理的时候就可以对 C(i,j) 取模,之后判断是不是为零就行了

O(n2+T)

#include 
#define N 2010

int A[N][N],n,m,k,t,Ans[N][N];

void reaD(int &x){
    char Ch=getchar();x=0;
    for(;Ch>'9'||Ch<'0';Ch=getchar());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=getchar());
}

int w[20],wt;

void Pt(int x){
    if(!x){putchar('0');putchar('\n');return ;}
    wt=0;while(x)w[++wt]=x%10,x/=10;
    for(;wt;wt--)putchar(w[wt]+'0');putchar('\n');
}

int main(){
    reaD(t);reaD(k);
    for(int i=0;i<=2000;i++) A[i][0]=1;A[1][1]=1;
    for(int i=2;i<=2000;i++){
        for(int j=1;j<=i;j++){
            if((A[i][j]=A[i-1][j-1]+A[i-1][j])>=k) A[i][j]-=k;
        }
    }
    for(int i=1;i<=2000;i++)for(int j=i+1;j<=2000;j++) A[i][j]=-1;
    for(int i=1;i<=2000;i++)
        for(int j=1;j<=2000;j++){
            Ans[i][j]=Ans[i-1][j]+Ans[i][j-1]-Ans[i-1][j-1]+(A[i][j]==0);
        }
    while(t--){
        reaD(n);reaD(m);
        Pt(Ans[n][m]);
    }
    return 0;
}

T2 earthworm

可以开三个队列,分别记录初始的蚯蚓和切开后的两断蚯蚓的长度,因为存储初始蚯蚓的队列按照升序排列,所以可以证明另外两个队列也是升序的。

那么每次找最长的只要比较三个队列的头部就行了。

至于怎么处理每个单位时间增加的长度,可以开个变量L,L每个单位时间加上q,新增的蚯蚓的长度只要减去q就行了。

O(nlogn+m)

#include 
#include 
#include 
#include 
#include 
#define N 100010
#define M 7000010
using namespace std;

int n,t,m,L,u,v,q,A[N],tt,T;
int B[M],C[M],lb,lc,tb,tc;

inline char NC(){
    static char buf[100000],*p1=buf,*p2=buf;
    if(p2==p1){
        p2=(p1=buf)+fread(buf,1,100000,stdin);
        if(p1==p2) return EOF;
    }
    return *p1++;
}

void reaD(int &x){
    char Ch=NC();x=0;
    for(;Ch>'9'||Ch<'0';Ch=NC());
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=NC());
}

int w[20],wt;

void Pt(int x){
    if(!x){putchar('0');return ;}
    wt=0;while(x)w[++wt]=x%10,x/=10;
    for(;wt;wt--)putchar(w[wt]+'0');
}

inline void Cut(int x,int ti){
    if(ti==tt){
        Pt(x+L),tt+=T;
        if(tt<=m) putchar(' ');
    }
    int a=1ll*(x+L)*u/v,b=x+L-a;L+=q;
    B[++tb]=a-L;C[++tc]=b-L;
}

inline void pt(int x,int ti){
    if(ti==tt){ 
        Pt(x),tt+=T;
        if(tt<=m+n) putchar(' ');
    }
}

inline bool cmp(const int &a,const int &b){
    return a>b;
}

int main(){
    reaD(n);reaD(m);reaD(q);reaD(u);reaD(v);reaD(T);tt=T;
    for(int i=1;i<=n;i++) reaD(A[i]);
    sort(A+1,A+1+n,cmp);lb=lc=t=1;
    for(int i=1;i<=m;i++){
        if(t<=n&&(A[t]>=B[lb]||lb>tb)&&(A[t]>=C[lc]||lc>tc)) Cut(A[t++],i);
        else if(lb<=tb&&(B[lb]>=C[lc]||lc>tc)) Cut(B[lb++],i);
        else Cut(C[lc++],i);
    }putchar('\n');
    int i=1;tt=T;
    while(t<=n||lb<=tb||lc<=tc){
        if(t<=n&&(A[t]>=B[lb]||lb>tb)&&(A[t]>=C[lc]||lc>tc)) pt(A[t++]+L,i);
        else if(lb<=tb&&(B[lb]>=C[lc]||lc>tc)) pt(B[lb++]+L,i);
        else pt(C[lc++]+L,i);
        i++;
    }
    return 0;
}

T3 angrybirds

状压DP

因为只有18只小鸟,可以把状态压缩在2^18的整数内。

但是这样DP的复杂度就是 O(2nn3)

所以我们可以枚举两只小鸟,计算以他们的坐标和原点坐标画成的抛物线上有哪些小鸟,也用2^18的整数记录下来。

这样复杂度就是 O(2nn2)

总复杂度 0(2nn2T)

#include 
#include 
#include 
#include 

using namespace std;

int n,m,l,r,mid,v[20],qt,tt,d[20][20],Res[1<<20],w;
double x[20],y[20];

struct prs{
    double a1,b1,a2,b2;
}q[30],p[20][20];

double abs(double x){
    if(x<0) return -x;
    return x;
}

prs Biu(int l,int s){
    prs Re;
    Re.a1=(y[l]*x[s]-y[s]*x[l]),Re.a2=(x[l]*x[l]*x[s]-x[s]*x[s]*x[l]);
    if(Re.a2==0) return Re;
    Re.b1=(y[l]-Re.a1*x[l]*x[l]/Re.a2),Re.b2=x[l];
    return Re;
}

void work(){
    scanf("%d%d",&n,&m);qt=0;
    for(int i=1;i<=n;i++) scanf("%lf %lf",&x[i],&y[i]),v[i]=0;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        if(x[i]!=x[j]){
            p[i][j]=Biu(i,j);
            d[i][j]=0;
            if(p[i][j].a1*p[i][j].a2>-1e-6) continue;
            for(int k=1;k<=n;k++)
                if(abs(p[i][j].a1*x[k]*x[k]/p[i][j].a2+p[i][j].b1*x[k]/p[i][j].b2-y[k])<=1e-6) d[i][j]|=1<1;
        }
    memset(Res,0x7F,sizeof(Res));
    Res[0]=0;
    for(int i=0,sx;i<=(1<for(int j=1;j<=n;j++){
            Res[i|(1<<(j-1))]=min(Res[i|(1<<(j-1))],Res[i]+1);
        }
        for(int j=1;j<=n;j++)
            if((i&(1<<(j-1)))==0)
            for(int k=j+1;k<=n;k++)
            if(x[j]!=x[k]&&(i&(1<<(k-1)))==0&&p[j][k].a1*p[j][k].a2<0){
                Res[i|d[j][k]]=min(Res[i|d[j][k]],Res[i]+1);
            }
    }
    printf("%d\n",Res[(1<1]);
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--) work();
    return 0;
}

你可能感兴趣的:(Presentation)