佳木斯市第一中学NOIP真题模拟赛(19.8.31—19.9.1)游记
%%%NOIP2016%%%
NOIP2016真题测试,日常总结成绩
Day1打了115菜的一匹,以为第二题能想出来,
Day2打了220,相比第一天,第二天的题简单了不少,但还是有思维难度
Day1 T1 玩具谜题
模拟和取模即可,上代码
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 100005
int n,m,x,y;
struct node
{
int head;
string name;
}e[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
cin>>e[i].head>>e[i].name;
}
int now=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
if(e[now].head==0 && x==0) now=(now+n-y)%n;
else if(e[now].head==0 && x==1) now=(now+y)%n;
else if(e[now].head==1 && x==0) now=(now+y)%n;
else if(e[now].head==1 && x==1) now=(now+n-y)%n;
}
cout<<e[now].name<<endl;
return 0;
}
Day1 T2 天天爱跑步
NOIP2016中最难的一道题了
首先考虑对于一条路径,我们如何去找答案,我们可以考虑LCA,那么我们求到了LCA后应该如何求解呢?
u表示起点,v表示终点
对于一条u到v的路径,先讨论LCA!=u && LCA!=v的情况:
分为u到LCA的路径和LCA到v的路径
对于u到LCA的路径上的点x,当deep[u]-deep[x]=w[x]时,即w[x]+deep[x]=deep[u]时,这条路径对点x有贡献;
观察发现w[x]+deep[x]是定值,所以统计经过x的路径中,deep[u]=w[x]+deep[x]的路径条数。
对于LCA到v的路径上的点x,当deep[u]-2deep[LCA]+deep[x]=w[x]时,即w[x]-deep[x]=deep[u]-2deep[lca]时,这条路径对点x有贡献;
观察发现w[x]-deep[x]是定值,所以统计经过x的路径中,deep[u]-2*deep[lca]=w[x]-deep[x]的路径条数;
接下来就是统计路径条数了,用到树上差分
我们统计的起点(终点)一定在点x子树内,所以统计x子树内有多少起点(终点)的值等于所需值
即统计有多少个在点x子树内的起点的deep[u]的值与deep[x]+w[x]相同
有多少终点的deep[u]-2*deep[lca]与w[x]-deep[x]相同
对于一个值,再u、v上加一个表示这个值+1的标记
考虑到x子树内的路径不一定经过x,所以在father[LCA]上加一个标记表示这个值-1
标记用动态数组储存
然后一遍dfs用两个桶分别统计,统计时值统一加上n,因为可能出现负数
记录下dfs到父亲节点时自己(也就是父亲的儿子)所需值的个数,然后统计完子树的值之后再做差计算自己
对于LCAu||LCAv的情况归于以上两类计算,特殊处理一下
另外,对于分裂成两条链LCA可能会被统计两遍,最后特殊判断一下,如果被统计了两遍就减去一遍。
上代码:
#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
int head[maxn*2],cnt[maxn*2],f[maxn][25],deep[maxn],t1[maxn*2],t2[maxn*2],s[maxn*2],t[maxn*2],w[maxn],lca[maxn];
int n,m,cntt=0;
struct node
{
int to,next;
}e[maxn*2];
struct tag
{
int v,siz;
};
vector<tag> tag1[maxn];
vector<tag> tag2[maxn];
inline int read()
{
int w=1,s=0; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
return w*s;
}
void add(int x,int y)
{
cntt++;
e[cntt].to=y;
e[cntt].next=head[x];
head[x]=cntt;
}
void dfs1(int u,int fa)
{
f[u][0]=fa; deep[u]=deep[fa]+1;
for(int i=1;i<=22;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
dfs1(v,u);
}
}
int LCA(int x,int y)
{
if(deep[x]<deep[y]) swap(x,y);
for(int i=22;i>=0;i--)
{
if(deep[f[x][i]]>=deep[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=22;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i]; y=f[y][i];
}
}
return f[x][0];
}
void dfs(int u,int a,int b)
{
for(int i=0;i<tag1[u].size();i++)
{
t1[tag1[u][i].v+maxn]+=tag1[u][i].siz;
}
for(int i=0;i<tag2[u].size();i++)
{
t2[tag2[u][i].v+maxn]+=tag2[u][i].siz;
}
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==f[u][0]) continue;
dfs(v,t1[w[v]+deep[v]+maxn],t2[w[v]-deep[v]+maxn]);
}
cnt[u]=cnt[u]+t1[w[u]+deep[u]+maxn]+t2[w[u]-deep[u]+maxn]-a-b;
}
int main()
{
//freopen("running.in","r",stdin);
//freopen("running.out","w",stdout);
n=read(); m=read();
for(int i=1;i<n;i++)
{
int a=read(),b=read();
add(a,b); add(b,a);
}
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<=m;i++)
{
s[i]=read(); t[i]=read();
}
dfs1(1,0);
for(int i=1;i<=m;i++) lca[i]=LCA(s[i],t[i]);
for(int i=1;i<=m;i++)
{
if(lca[i]==t[i])
{
tag1[s[i]].push_back((tag){deep[s[i]],1});
tag1[f[t[i]][0]].push_back((tag){deep[s[i]],-1});
}
else if(lca[i]==s[i])
{
tag2[t[i]].push_back((tag){deep[s[i]]-2*deep[lca[i]],1});
tag2[f[s[i]][0]].push_back((tag){deep[s[i]]-2*deep[lca[i]],-1});
}
else
{
if(w[lca[i]]+deep[lca[i]]==deep[s[i]]) --cnt[lca[i]];
tag1[s[i]].push_back((tag){deep[s[i]],1});
tag1[f[lca[i]][0]].push_back((tag){deep[s[i]],-1});
tag2[t[i]].push_back((tag){deep[s[i]]-2*deep[lca[i]],1});
tag2[f[lca[i]][0]].push_back((tag){deep[s[i]]-2*deep[lca[i]],-1});
}
}
dfs(1,0,0);
for(int i=1;i<=n;i++) printf("%d ",cnt[i]);
return 0;
}
Day1 T3 换教室
emmmmmmm概率DP可是我不会
考后看题解发现:
除了方程长一些好像其他还好
我们分情况讨论
设f[i][j][0\1]表示选到第i个,选了j个,当前选\不选
一:当前不选
如果上一个选:1、可能上一个成功,2、也可能失败
如果上一个不选,那么直接加上c[i-1]与c[i]的最短路
二:当前选
如果上一个选:1、都成功。2、前成功后失败。3、前失败后成功。4、都失败~~(太失败了)~~
如果上一个不选:1、当前换成功。2、当前换失败
所以方程就出来啦
上代码,方程很长,分了几行:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 2005
#define maxm 2005
#define maxv 305
int dis[maxv][maxv],c[maxn],d[maxn];
int n,m,v,e;
double k[maxn],f[maxn][maxm][2];
inline int read()
{
int w=1,s=0; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
return w*s;
}
void Floyd()
{
for(int k=1;k<=v;k++)
{
for(int i=1;i<=v;i++)
{
for(int j=1;j<i;j++)
{
if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[j][i]=dis[i][k]+dis[k][j];
}
}
}
}
int main()
{
n=read(); m=read(); v=read(); e=read();
for(int i=1;i<=n;i++) c[i]=read();
for(int i=1;i<=n;i++) d[i]=read();
for(int i=1;i<=n;i++) scanf("%lf",&k[i]);
for(int i=1;i<=v;i++)
{
for(int j=1;j<i;j++) dis[i][j]=dis[j][i]=INF;
}
for(int i=1;i<=e;i++)
{
int x=read(),y=read(),z=read();
dis[x][y]=min(dis[x][y],z);
dis[y][x]=dis[x][y];
}
Floyd();
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
f[i][j][0]=f[i][j][1]=INF;
f[1][0][0]=f[1][1][1]=0;
for(int i=2;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
f[i][j][0]=min(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-1][j][0]+dis[c[i-1]][c[i]]);
if(j!=0) f[i][j][1]=min(f[i-1][j-1][1]+k[i-1]*k[i]*
dis[d[i-1]][d[i]]+k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+
(1-k[i-1])*(1-k[i])*dis[c[i-1]][c[i]],f[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1-k[i])*dis[c[i-1]][c[i]]);
}
}
double ans=INF;
for(int i=0;i<=m;i++)
{
ans=min(ans,min(f[n][i][0],f[n][i][1]));
}
printf("%.2lf",ans);
return 0;
}
Day2 T1 组合数问题
学过组合数和矩阵前缀和应该都会吧
#include <bits/stdc++.h>
using namespace std;
#define maxn 2005
#define ll long long
ll C[maxn][maxn],ans[maxn][maxn];
ll n,m,k;
inline ll read()
{
ll w=1,s=0; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
return w*s;
}
void init()
{
C[0][0]=1;
C[1][0]=C[1][1]=1;
for(int i=2;i<=2002;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%k;
}
}
for(int i=1;i<=2002;i++)
{
for(int j=1;j<=i;j++)
{
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];
if(!C[i][j]) ans[i][j]++;
}
ans[i][i+1]=ans[i][i];
}
}
int main()
{
freopen("problem.in","r",stdin);
freopen("problem.out","w",stdout);
int t=read(); k=read();
init();
while(t--)
{
n=read(); m=read();
if(m>n) printf("%lld\n",ans[n][n]);
else printf("%lld\n",ans[n][m]);
}
return 0;
}
Day2 T2 蚯蚓
暴力显然就是堆+模拟
正解我们可以发现数据本身具有单调性,先被切断的一定比后被切断的长
所以维护三个队列,分别存入初始、切完后的a1和a2
每次从三个队列中找最大(即三个队首元素最大)
上代码:
#include <bits/stdc++.h>
using namespace std;
#define maxn 7000005
int cut1[maxn],cut2[maxn],a[maxn];
int n,m,q,u,v,t,top,s=0;
inline int read()
{
int w=1,s=0; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
return w*s;
}
bool cmp(int a,int b)
{
return a>b;
}
priority_queue<int> ans;
int main()
{
n=read(); m=read(); q=read(); u=read(); v=read(); t=read();
double p=1.0*u/v;
int t0,t1,t2,h,h1,h2;
for(t0=1;t0<=n;t0++) a[t0]=read();
--t0; t1=t2=0; h=h1=h2=1;
sort(a+1,a+t0+1,cmp);
for(int i=1;i<=m;i++)
{
if(h>t0)
{
if(cut1[h1]>cut2[h2]) top=cut1[h1],h1++;
else top=cut2[h2],h2++;;
}
else if(a[h]>=cut1[h1] && a[h]>=cut2[h2]) top=a[h],h++;
else if(cut1[h1]>=cut2[h2] && a[h]<=cut1[h1]) top=cut1[h1],h1++;
else top=cut2[h2],h2++;
top+=s;
if(i%t==0) printf("%d ",top);
int a1=floor(p*(double)top),a2=top-a1;
s+=q;
a1-=s; a2-=s;
cut1[++t1]=a1; cut2[++t2]=a2;
}
printf("\n");
for(int i=h;i<=t0;i++) ans.push(a[i]);
for(int i=h1;i<=t1;i++) ans.push(cut1[i]);
for(int i=h2;i<=t2;i++) ans.push(cut2[i]);
int sz=ans.size();
for(int i=1;i<=sz;i++)
{
int x=ans.top(); ans.pop();
if(i%t==0) printf("%d ",x+s);
}
return 0;
}
Day2 T3
n<=18,不是状态压缩就是爆搜
我们毅然决然选择暴力搜索
对于一头猪,要么和前面的组成抛物线,要么自己单独
那么我们可以把所有情况搜索出来
然后求取最小值
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define maxn 25
#define INF 0x3f3f3f3f
#define ep 0.0000001
double xx[maxn],yy[maxn],rx[maxn],ry[maxn],pwxa[maxn],pwxb[maxn];
int ans,n,m;
bool equal(double a,double b)
{
if(fabs(a-b)<ep) return true;
return false;
}
void dfs(int pos,int u,int v)//u是前pos组成抛物线个数,v是前pos单独的个数
{
if(u+v>ans) return ;
if(pos>n) ans=min(ans,u+v);
double x=xx[pos],y=yy[pos];
bool flag=false;
for(int i=1;i<=u;i++)
{
double a=pwxa[i],b=pwxb[i];
if(equal(y,1.0*a*x*x+1.0*b*x)==true) {flag=true; break;}
}
if(!flag)
{
for(int i=1;i<=v;i++)
{
double x1=rx[i],y1=ry[i];
if(equal(x,x1)==true) continue;
double aa=1.0*(x1*y-x*y1)/(x1*x*x-x1*x1*x);
double bb=1.0*(y-aa*x*x)/x;
if(aa<0.0)
{
pwxa[u+1]=aa; pwxb[u+1]=bb;
double qx=x1,qy=y1;
for(int j=i;j<v;j++)
{
rx[j]=rx[j+1]; ry[j]=ry[j+1];
}
dfs(pos+1,u+1,v-1);
for(int j=v;j>i;j--)
{
rx[j]=rx[j-1]; ry[j]=ry[j-1];
}
pwxa[u+1]=pwxb[u+1]=0;
rx[i]=qx; ry[i]=qy;
}
}
rx[v+1]=x; ry[v+1]=y;
dfs(pos+1,u,v+1);
rx[v+1]=0; ry[v+1]=0;
}
else dfs(pos+1,u,v);
}
int main()
{
//freopen("angrybirds.in","r",stdin);
//freopen("angrybirds.out","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lf%lf",&xx[i],&yy[i]);
ans=INF;
dfs(1,0,0);
printf("%d\n",ans);
}
return 0;
}