NOIP2016提高组Day1题解

来源:互联网 发布:淘宝外观专利投诉流程 编辑:程序博客网 时间:2024/05/17 05:11

前言

(我去年还是普及组蒟蒻呢……)
最近模拟考了一下NOIP2016提高组Day1,结果只搞了156分……太弱了。
T1:100分 T2:40分 T3:16分
其中T3的v打成n,然后就炸掉了,本来可以80(然后就上200了啊,T_T)。

玩具谜题

解题报告

水题,应该是用来送分的吧。只需要注意越界时候的处理就行了。
不过如果使用字符数组,要多开一个用来存’\0’,否则就会炸掉。

示例程序

#include<cstdio>using namespace std;const int maxn=100000;const int fl[2][2]={{-1,1},{1,-1}};int n,te,pos;struct Toy {int f;char s[11];};Toy a[maxn+5];bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}int readi(int &x){    int tot=0,f=1;char ch=getchar(),lst='+';    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}    if (lst=='-') f=-f;    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();    x=tot*f;    return Eoln(ch);}int reads(char *s){    int len=0;char ch=getchar();if (ch==EOF) return EOF;    while ('z'<ch||ch<'a') ch=getchar();    while ('a'<=ch&&ch<='z') s[len++]=ch,ch=getchar();s[len]=0;    return len;}int main(){    freopen("toy.in","r",stdin);    freopen("toy.out","w",stdout);    readi(n);readi(te);pos=0;    for (int i=0;i<n;i++) readi(a[i].f),reads(a[i].s);    while (te--)    {        int f,s;readi(f);readi(s);        pos+=fl[a[pos].f][f]*s;        if (pos<0) pos+=n;if (pos>=n) pos-=n;    }    printf("%s\n",a[pos].s);    return 0;}

天天爱跑步

解题报告

(乘机水掉BZOJ4719)
好像是Day1(和Day2?)最难的题目。

处理子问题的时候我们会发现很多暗示:比如链的时候满足观察员x的节点只能为x+w[x]或x-w[x],S=1和T=1实际上在告诉我们把S->T分一下。
结合一下,我们有这么一个思路:把S->T分一下,不要直接看成S->T,且把满足的节点转化为代数式。

S->T怎么分呢?分成S->LCA和LCA->T比较好,因为这样分就得到了两条直链,没有拐弯。再来看满足节点的转化。
1.路径S->LCA上的满足节点x
不难得出,当dep[S]-dep[x]=w[x](且LCA不在x下面)时,从S出发能恰好到x,移项得到dep[S]=dep[x]+w[x]。
2.路径LCA->T上的满足节点x
这个其实也是很简单的,记S->T的长度为dis,那么当dep[T]-dep [x]=dis-w[x](且LCA不在x下面)时,从S出发能经过LCA并恰好到x,移项得到dep[T]-dis=dep[x]-w[x]。

所以我们可以记录hash[k]表示k这个值出现了几次(有负数向右移即可),然后Dfs处理。以S->LCA为例,对于节点x,先处理x的儿子的子树,遇到S就hash[dep[S]]++,返回来遇到S的LCA就hash[dep[S]]–。最终返回到x时,就得到了当前的hash[k]数组,此时统计hash[dep[x]+w[x]]就行了?其实是不行的,因为有不符合x的节点,也就是LCA在x上面的节点。这怎么办?我们可以记录lst表示刚遇到x时的hash[dep[x]+w[x]],然后处理完x的儿子的子树后hash[dep[x]+w[x]]-lst就是满足的答案了(容斥大法好)。而对于LCA->T也是一样的。

然而到这里并没有结束,这个算法还有个小Bug,就是由于处理的时候处理了S->LCA和LCA->T,LCA被处理了两次!有两种解决方法,第一种就是最后去下重(如果LCA在S->LCA和LCA->t里都是满足节点,就把答案-1),第二种就是刚开始拆路径的时候就不把LCA拆两次。

我的程序好像不是特别快,果然还是太弱了……

示例程序

#include<cstdio>#include<cmath>#include<vector>using namespace std;const int maxn=299998,maxm=299998,maxt=19;int n,m,w[maxn+5],st[maxm+5],gl[maxm+5],lca[maxm+5],ans[maxn+5];int f[maxn+5][maxt+5],dep[maxn+5],ha[3*maxn+5];int E,lnk[maxn+5],son[2*maxn+5],nxt[2*maxn+5];struct AdjList{    int E,lnk[maxn+5],son[maxn+5],nxt[maxn+5];    void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];lnk[x]=E;}};AdjList A,B,C,D;bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}int readi(int &x){    int tot=0,f=1;char ch=getchar(),lst='+';    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}    if (lst=='-') f=-f;    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();    x=tot*f;    return Eoln(ch);}void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];lnk[x]=E;}void Dfs(int x,int fa){    for (int j=lnk[x];j;j=nxt[j])        if (son[j]!=fa)        {            f[son[j]][0]=x;dep[son[j]]=dep[x]+1;            Dfs(son[j],x);        }}void make_f(){    int k=log2(n);    for (int j=1;j<=k;j++)    for (int i=1;i<=n;i++)        f[i][j]=f[f[i][j-1]][j-1];}int LCA(int x,int y){    if (dep[x]<dep[y]) swap(x,y);    for (int i=maxt;i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i];    if (x==y) return x;    for (int i=maxt;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];    return f[x][0];}int getr(int x) {return x+maxn;}void Count1(int x,int fa){    int lst=ha[getr(dep[x]+w[x])];    for (int j=lnk[x];j;j=nxt[j])        if (son[j]!=fa) Count1(son[j],x);    for (int j=A.lnk[x];j;j=A.nxt[j]) ha[getr(A.son[j])]++;    ans[x]+=ha[getr(dep[x]+w[x])]-lst;    for (int j=C.lnk[x];j;j=C.nxt[j]) ha[getr(C.son[j])]--;}void Count2(int x,int fa){    int lst=ha[getr(dep[x]-w[x])];    for (int j=lnk[x];j;j=nxt[j])        if (son[j]!=fa) Count2(son[j],x);    for (int j=B.lnk[x];j;j=B.nxt[j]) ha[getr(B.son[j])]++;    ans[x]+=ha[getr(dep[x]-w[x])]-lst;    for (int j=D.lnk[x];j;j=D.nxt[j]) ha[getr(D.son[j])]--;}int main(){    freopen("running.in","r",stdin);    freopen("running.out","w",stdout);    readi(n);readi(m);    for (int i=1;i<=n-1;i++)    {        int x,y;readi(x);readi(y);        Add(x,y);Add(y,x);    }    for (int i=1;i<=n;i++) readi(w[i]);    dep[1]=1;Dfs(1,-1);make_f();    for (int i=1;i<=m;i++)    {        int dis;readi(st[i]);readi(gl[i]);        lca[i]=LCA(st[i],gl[i]);dis=dep[st[i]]+dep[gl[i]]-2*dep[lca[i]];        A.Add(st[i],dep[st[i]]);B.Add(gl[i],dep[gl[i]]-dis);        C.Add(lca[i],dep[st[i]]);D.Add(lca[i],dep[gl[i]]-dis);    }    Count1(1,-1);Count2(1,-1);    for (int i=1;i<=m;i++) if (dep[st[i]]-dep[lca[i]]==w[lca[i]]) ans[lca[i]]--;    for (int i=1;i<=n;i++)        if (i==n) printf("%d\n",ans[i]); else printf("%d ",ans[i]);    return 0;}

换教室

解题报告

(乘机水掉BZOJ4720)
这道题其实是不难的期望DP,但是我太弱了,不知道期望的线性性,于是感觉DP完全没法写,打暴力去了(暴力还打挂,蒟蒻+=∞)。
定义f[i][j][0/1]表示前i个时间段,选了j次变更,其中第i次没变更(0)或变更(1)了。
转移方程很容易,但利用了期望的线性性。设c代表原先教室,d代表更换教室,dXY表示i-1的教室为X,i的教室为Y,X->Y的路程,P1表示i-1更换成功的概率,P2表示i更换成功的概率。则:

f[i][j][0]=min(A,B);A=f[i-1][j][0]+dcc;//i-1和i都不变更,概率为100%B=f[i-1][j][1]+P1*ddc+(1-P1)*dcc;//i-1变更,成功或失败都要算f[i][j][1]=min(A,B);A=f[i-1][j-1][0]+P2*dcd+(1-P2)*dcc;//i变更,成功或失败都要算B=f[i-1][j-1][1]+P2*(P1*ddd+(1-P1)*dcd)+(1-P2)*(P1*ddc+(1-P1)*dcc);//i-1和i都变更,成功或失败都要算

示例程序

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=2000,maxv=300;int n,m,v,e,C[maxn+5],D[maxn+5];double K[maxn+5],ans=1e100,f[maxn+5][maxn+5][2];int dis[maxv+5][maxv+5];bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}int readi(int &x){    int tot=0,f=1;char ch=getchar(),lst='+';    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}    if (lst=='-') f=-f;    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();    x=tot*f;    return Eoln(ch);}int readd(double &x){    double tot=0,f=1,Base=1;char ch=getchar(),lst='+';    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}    if (lst=='-') f=-f;    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();    if (ch=='.')    {        ch=getchar();        while ('0'<=ch&&ch<='9') tot+=(ch-48)/(Base*=10),ch=getchar();    }    x=tot*f;    return Eoln(ch);}void Floyd(){    for (int k=1;k<=v;k++)    for (int i=1;i<=v;i++)    for (int j=1;j<=v;j++)        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}int main(){    freopen("classroom.in","r",stdin);    freopen("classroom.out","w",stdout);    readi(n);readi(m);readi(v);readi(e);    for (int i=1;i<=n;i++) readi(C[i]);    for (int i=1;i<=n;i++) readi(D[i]);    for (int i=1;i<=n;i++) readd(K[i]);    memset(dis,63,sizeof(dis));    for (int i=1;i<=v;i++) dis[0][i]=0;    for (int i=1;i<=v;i++) dis[i][i]=0;    for (int i=1;i<=e;i++)    {        int x,y,z;readi(x);readi(y);readi(z);        dis[x][y]=min(dis[x][y],z);dis[y][x]=min(dis[y][x],z);    }    Floyd();memset(f,127,sizeof(f));    double INF=f[0][0][0];f[0][0][0]=0;    for (int i=1;i<=n;i++)    {        f[i][0][0]=f[i-1][0][0]+dis[C[i-1]][C[i]];        for (int j=1;j<=min(i,m);j++)        {            double A,B;            A=f[i-1][j][0]+dis[C[i-1]][C[i]];            B=f[i-1][j][1]+K[i-1]*dis[D[i-1]][C[i]]+(1-K[i-1])*dis[C[i-1]][C[i]];            f[i][j][0]=min(A,B);            A=f[i-1][j-1][0]+K[i]*dis[C[i-1]][D[i]]+(1-K[i])*dis[C[i-1]][C[i]];            B=f[i-1][j-1][1]+K[i]*(K[i-1]*dis[D[i-1]][D[i]]+(1-K[i-1])*dis[C[i-1]][D[i]])+(1-K[i])*(K[i-1]*dis[D[i-1]][C[i]]+(1-K[i-1])*dis[C[i-1]][C[i]]);            f[i][j][1]=min(A,B);        }    }    for (int j=0;j<=m;j++) ans=min(ans,min(f[n][j][0],f[n][j][1]));    printf("%.2lf\n",ans);    return 0;}
原创粉丝点击