欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-18 21:54:28
...


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;
}