@01010101
2018-11-05T21:37:26.000000Z
字数 5043
阅读 866
noip
DAY1T1玩具谜题
按题意模拟即可。
DAY1T2天天爱跑步
开始少考虑了一种情况。
确实是需要树上差分的。
少的情况:
s和t都在以fa[lca]为根的子树中。但此时s和t不能对fa[lca]产生贡献。
这就是树上差分和树上直接用桶维护的区别。
桶的要求是子树中的每个值都能对答案产生贡献。
树上差分:只对u到v的路径上的点有贡献。(对lca以上的点没有贡献)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int read(){
char ch;int op=1;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') op=-1;
int ret=ch-'0';
while((ch=getchar())>='0'&&ch<='9') ret=ret*10+ch-'0';
return ret*op;
}
void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar('0'+x%10);
}
const int N=300000+10;
const int D=25;
int n,m,kk;
int s[N],t[N],w[N],vis[N],val[N],dep[N],head[N],A[N<<1],B[N<<1],f[N][D+2];
struct edge{
int v,nxt;
}e[N<<1];
void adde(int u,int v){
e[kk].v=v;
e[kk].nxt=head[u];
head[u]=kk++;
}
vector <int> v[N];
vector <int> vec[N];
vector <int> vst[N];
void dfs(int u,int fa){
dep[u]=dep[fa]+1;f[u][0]=fa;
for(int i=1;i<=D;++i)
f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i!=-1;i=e[i].nxt)
if(e[i].v!=fa)
dfs(e[i].v,u);
}
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=D;i>=0;--i)
if(dep[f[u][i]]>=dep[v])
u=f[u][i];
if(u==v) return u;
for(int i=D;i>=0;--i)
if(f[u][i]!=f[v][i]){
u=f[u][i];
v=f[v][i];
}
return f[u][0];
}
void dfs1(int u,int fa){
int tmpa=A[dep[u]+w[u]];
int tmpb=B[w[u]-dep[u]+n];
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(v!=fa){
dfs1(v,u);
}
}
if(vis[u]) A[dep[u]]+=vis[u];
int sz=v[u].size();
for(int i=0;i<sz;++i){
B[v[u][i]+n]++;
}
val[u]+=A[dep[u]+w[u]]-tmpa;
val[u]+=B[w[u]-dep[u]+n]-tmpb;
sz=vec[u].size();
for(int i=0;i<sz;++i){
B[vec[u][i]+n]--;
}
sz=vst[u].size();
for(int i=0;i<sz;++i){
A[vst[u][i]]--;
}
}
int main(){
// freopen("test.in","r",stdin);
// freopen("test.out","w",stdout);
int a,b,l;
memset(head,-1,sizeof(head));
n=read();m=read();
for(int i=1;i<n;++i){
a=read();b=read();
adde(a,b);adde(b,a);
}
for(int i=1;i<=n;++i){
w[i]=read();
}
dfs(1,0);
for(int i=1;i<=m;++i){
s[i]=read();t[i]=read();
l=lca(s[i],t[i]);
if(l!=s[i]&&l!=t[i]){
vis[s[i]]++;
vst[l].push_back(dep[s[i]]);
v[t[i]].push_back(dep[s[i]]-2*dep[l]);
vec[l].push_back(dep[s[i]]-2*dep[l]);
if(dep[l]+w[l]==dep[s[i]]) val[l]--;
}
else {
if(l==s[i]&&l!=t[i]){
v[t[i]].push_back(-dep[s[i]]);
vec[l].push_back(-dep[s[i]]);
}
else if(l==t[i]&&l!=s[i]){
vis[s[i]]++;
vst[l].push_back(dep[s[i]]);
}
else{
if(w[s[i]]==0) val[s[i]]++;
}
}
}
dfs1(1,0);
for(int i=1;i<=n;++i){
write(val[i]);putchar(' ');
}
return 0;
}
DAY1T3换教室
相对简单的概率dp,一定要把所有情况列举出来
dp[i][j][1/0] 表示到第i节课,换了j节课,1表示当前申请了换课,0表示当前没申请换课
1.当前的课不换的情况:(1)上一节课也没换;(2)上一节课换了----成功or不成功;
2.当前的课换的情况:
(1)当前课成功换了:a.上一节课换了----上一节课成功or不成功b.上一节课没换;
(2)当前的课换了失败:a.上一节课换了----上一节课成功or不成功b.上一节课没换;
DAY2T1组合数问题
法一:先把K分解质因数,然后二维递推每个组合数中含这些因子的情况。(先处理每个数,在处理组合数),最后O(1)查询。(主要是观察到K最大为21,质因数就2个)
法二:预处理杨辉三角(模k),然后处理出二维前缀和,最后O(1)查询。
DAY2T2蚯蚓
关键是要发现强制增长的性质。一段蚯蚓在切断后之前的增长就没有了,然后就开始想取维护一个标记,表示在什么时候开始增长。可是这样就不能保证用优先队列找出的最大值准确。(好像也可以,在重定义小于符号的时候用(当前时刻-开始累计的时刻)*系数+开始长度)。所以就让每个蚯蚓强制从时刻1开始增长,所以最开始的长度可能是负数。
最开始想用线段树求第K大。但是高达1e8就没有办法了,而且如果强制增长会出现负数。也没有办法离散化。
开始没注意数据范围就用优先队列了,TLE了3个点。
看了题解发现就是一个小性质。维护3个队列。第一个是原队列,第二个是切后长蚯蚓队列,第三个是切后短蚯蚓队列。这三个队列分别满足单调性。
queue <int> q[3];
int main(){
// freopen("a.txt","r",stdin);
n=read();m=read();qq=read();
u=read();v=read();t=read();
p=(double)u/v;
for(int i=1;i<=n;++i) a[i]=read();
sort(a+1,a+n+1);
for(int i=1;i<=n;++i) q[0].push(a[n-i+1]);
while(m--){
A=-INF;B=-INF;C=-INF;
if(!q[0].empty())A=q[0].front();
if(!q[1].empty())B=q[1].front();
if(!q[2].empty())C=q[2].front();
if(A>=B&&A>=C){ps=A;q[0].pop();}
else if(B>=A&&B>=C){ps=B;q[1].pop();}
else {ps=C;q[2].pop();}
ps+=delta;
if(++cnt==t){
cnt=0;
write(ps);putchar(' ');
}
nw1=ps*p,nw2=ps-nw1;
delta+=qq;
nw1-=delta;
nw2-=delta;
if(nw1>=nw2){
q[1].push(nw1);
q[2].push(nw2);
}
else{
q[1].push(nw2);
q[2].push(nw1);
}
}
putchar('\n');
cnt=0;
while(!q[0].empty()||!q[1].empty()||!q[2].empty()){
A=-INF;B=-INF;C=-INF;
if(!q[0].empty())A=q[0].front();
if(!q[1].empty())B=q[1].front();
if(!q[2].empty())C=q[2].front();
if(A>=B&&A>=C){ps=A;q[0].pop();}
else if(B>=A&&B>=C){ps=B;q[1].pop();}
else {ps=C;q[2].pop();}
if(++cnt==t){
ps+=delta;
cnt=0;
write(ps);putchar(' ');
}
}
return 0;
}
DAY2T3愤怒的小鸟
状压。没有想象中的难。
关键在于要发现三点可以确定一条抛物线。一点是起点,所以我们定义一条抛物线就是cover[i][j]就是经过i点和j点的那条抛物线经过的点的集合(状压)。
int main(){
T=read();
while(T--){
n=read();m=read();
t=(1<<n)-1;
for(int i=1;i<=n;++i)
scanf("%lf%lf",&a[i].x,&a[i].y);
memset(cover,0,sizeof(cover));
for(int i=1;i<=n;++i){
cover[i][i]=(1<<(i-1));
double xi=a[i].x,yi=a[i].y;
for(register int j=1;j<=n;++j){
if(fabs(a[i].x-a[j].x)<eps) continue;
double xj=a[j].x,yj=a[j].y;
double B=(yj*xi*xi-yi*xj*xj)/(xi*xi*xj-xi*xj*xj);
double A=(yi-xi*B)/(xi*xi);
if(A>-eps) continue;
for(register int k=1;k<=n;++k)
if(fabs(a[k].y-A*a[k].x*a[k].x-B*a[k].x)<eps)
cover[i][j]|=(1<<(k-1));
}
}
memset(dp,INF,sizeof(dp));
dp[0]=0;
for(register int i=1;i<=n;++i)
for(register int j=0;j<=t;++j)
if(dp[j]<INF)
for(register int k=1;k<=n;++k)
dp[(j|cover[i][k])]=min(dp[(j|cover[i][k])],dp[j]+1);
printf("%d\n",dp[t]);
}
return 0;
}
由此,noip2016所有题就写完了。
模拟
树上差分
概率dp
数学
数据结构(队列)+思维
状压dp