前言
(我去年还是普及组蒟蒻呢……)
最近模拟考了一下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;
}