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;}
- NOIP2016提高组Day1题解
- NOIP2016提高组 DAY1
- NOIP2016提高组day1
- NOIP2016提高组Day1
- NOIP2016 提高组 题解
- NOIP2016 提高组 题解
- NOIP2016 提高组 题解
- NOIP2016提高组题解
- NOIP2016提高组题解
- 【NOIP2016提高组day1】换教室
- NOIP2016提高组day1 换教室
- NOIP2016提高组Day1 真题测试
- NOIP2014提高组DAY1题解
- [题解]NOIP2016提高组の题解集合
- noip2016提高&普及组简要题解
- {题解}[jzoj4907]【NOIP2016提高组复赛】蚯蚓
- NOIP2016 提高组题解口胡
- NOIP2012提高组day1 vigenere密码 题解
- C++11参考资料
- 数据库--(4)
- 递归转二进制
- ArrayList遍历(JAVA)
- uboot位置无关码码
- NOIP2016提高组Day1题解
- C++抽象编程——指针(1)——什么是指针?
- Opentext BPS (Cordys) Eclipse开发环境部署实践
- 动态规划题集(转载)
- TortoiseSVN 下载与安装
- MySQL常用语法总结
- 安装数据库软件时配置内核参数报错
- 常见的异常类型
- nginx配置负载均衡