佳木斯市第一中学NOIP真题模拟赛(19.8.31—19.9.1)游记


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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值