NOIP2016题解

D1T1:把方向和朝向异或一下,在mod n意义下+1s或-1s。

#include<cstdio>
const int N=1e5+5;
int n,m,j,k,v,s[N];
char t[N][11];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;++i)
		scanf("%d%s",s+i,t[i]);
	while(m--){
		scanf("%d%d",&j,&k);
		(v+=j^s[v]?k:n-k)%=n;
	}
	puts(t[v]);
}

D1T2:设i的深度为d[i],点i的答案是s[j]在其子树中,d[s[j]]==d[i]+w[i],且lca[j]在其子树外或等于i的j的个数,加上t[j]在其子树中,d[lca[j]]*2-d[s]==d[i]-w[i],且lca[j]在其子树外(s[j]在子树外)的j的个数。考虑差分链,++a[s[i]],--a[lca[j]的父亲],++b[t[j]],--b[lca[j]],那么查询子树中对应的a的和和b的和就是答案。通过dfs序,转化为查询区间中等于一个数的数的权和,那么差分询问,设查询[l,r],答案就是r处的答案减去l-1处的答案。

#include<cstdio>
const int N=3e5+5;
struct edge{
	int v;edge*s;
}e[N*2];
edge*o=e,*h[N];
void ins(int u,int v){
	edge s={v,h[u]};
	*(h[u]=o++)=s;
}
typedef int arr[N];
arr d,p,r,c,y,l,q;
int z0[N*2],z1[N*2],*z2=z1+N;
void dfs1(int u){
	r[u]=1;
	for(edge*i=h[u];i;i=i->s)
		if(i->v^p[u]){
			d[i->v]=d[p[i->v]=u]+1;
			dfs1(i->v);
			r[c[u]]<r[i->v]
			?c[u]=i->v:0;
			r[u]+=r[i->v];
		}
}
void dfs2(int u,int f){
	static int t;
	l[u]=++t,y[u]=f;
	if(!c[u])return;
	dfs2(c[u],y[u]);
	for(edge*i=h[u];i;i=i->s)
		if(i->v^p[u]&&i->v^c[u])
			dfs2(i->v,i->v);
}
int lca(int s,int t){
	while(y[s]^y[t]){
		if(d[y[s]]<d[y[t]])
			s^=t,t^=s,s^=t;
		s=p[y[s]];
	}
	return d[s]<d[t]?s:t;
}
struct foo{
	int z,x,y;foo*s;
}e2[N*4];
foo*o2=e2,*h2[N],*h3[N];
#define ins2(v,...){	foo e={__VA_ARGS__,v};	*(v=o2++)=e;}
struct bar{
	int x,y;bar*s;
}e3[N*4];
bar*o3=e3,*h4[N],*h5[N];
#define ins3(v,...){	bar e={__VA_ARGS__,v};	*(v=o3++)=e;}
int n,m,s,t;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;++i){
		scanf("%d%d",&s,&t);
		ins(s,t),ins(t,s);
	}
	dfs1(1),dfs2(1,1);
	for(int i=1;i<=n;++i){
		scanf("%d",&s);
		int x=l[i]-1,y=x+r[i];
		ins2(h2[x],i,d[i]+s,-1);
		ins2(h2[y],i,d[i]+s,+1);
		ins2(h3[x],i,d[i]-s,-1);
		ins2(h3[y],i,d[i]-s,+1);
	}
	while(m--){
		scanf("%d%d",&s,&t);
		int i=lca(s,t),j=p[i];
		int x=d[s],y=d[i]*2-x;
		ins3(h4[l[s]],x,+1);
		ins3(h4[l[j]],x,-1);
		ins3(h5[l[t]],y,+1);
		ins3(h5[l[i]],y,-1);
	}
	for(int i=1;i<=n;++i){
		for(bar*j=h4[i];j;j=j->s)
			z1[j->x]+=j->y;
		for(bar*j=h5[i];j;j=j->s)
			z2[j->x]+=j->y;
		for(foo*j=h2[i];j;j=j->s)
			q[j->z]+=z1[j->x]*j->y;
		for(foo*j=h3[i];j;j=j->s)
			q[j->z]+=z2[j->x]*j->y;
	}
	for(int i=1;i<=n;++i)
		printf("%d ",q[i]);
}

D1T3:设f[i][j][0/1]表示前i节课,申请了j次,第i节课没申请/申请了的期望。floyd预处理两点间最短路。讨论所有情况,按概率加权直接转移。

#include<cstdio>
#include<cstring>
#include<algorithm>
using std::min;
const int N=2005;
int n,m,v,e,a,b,w;
int c[N],d[N],z[305][305];
double p[N],f[N][N][2];
template<class T>
void eq1(T&a,T b){a=b<a?b:a;}
int main(){
	scanf("%d%d%d%d",&n,&m,&v,&e);
	for(int i=1;i<=n;++i)
		scanf("%d",c+i);
	for(int i=1;i<=n;++i)
		scanf("%d",d+i);
	for(int i=1;i<=n;++i)
		scanf("%lf",p+i);
	memset(z,63,sizeof z);
	for(int i=1;i<=v;++i)
		z[i][i]=0;
	while(e--){
		scanf("%d%d%d",&a,&b,&w);
		eq1(z[a][b],w);
		eq1(z[b][a],w);
	}
	for(int k=1;k<=v;++k)
		for(int i=1;i<=v;++i)
			for(int j=1;j<=v;++j)
				eq1(z[i][j],z[i][k]+z[k][j]);
	for(int i=1;i<=n;++i)
		for(int j=0;j<=m;++j)
			f[i][j][0]=f[i][j][1]=1e18;
	f[1][0][0]=f[1][1][1]=0;
	for(int i=2;i<=n;++i){
		f[i][0][0]=f[i-1][0][0]+z[c[i-1]][c[i]];
		for(int j=1;j<=i;++j){
			f[i][j][0]=min(f[i-1][j][0]+z[c[i-1]][c[i]],f[i-1][j][1]+z[c[i-1]][c[i]]*(1-p[i-1])+z[d[i-1]][c[i]]*p[i-1]);
			f[i][j][1]=min(f[i-1][j-1][0]+z[c[i-1]][c[i]]*(1-p[i])+z[c[i-1]][d[i]]*p[i],f[i-1][j-1][1]+z[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+z[d[i-1]][c[i]]*p[i-1]*(1-p[i])+z[c[i-1]][d[i]]*(1-p[i-1])*p[i]+z[d[i-1]][d[i]]*p[i-1]*p[i]);
		}
	}
	double ans=1e18;
	for(int j=0;j<=m;++j)
		eq1(ans,min(f[n][j][0],f[n][j][1]));
	printf("%.2f\n",ans);
}

D2T1:预处理$[\binom{i}{j}\equiv0\pmod{k}]$的二维前缀和。

#include<cstdio>
const int N=2005;
int t,p,n,m;
int c[N][N],s[N][N];
int main(){
	scanf("%d%d",&t,&p);
	for(int i=0;i<N;++i){
		c[i][0]=1;
		for(int j=1;j<=i;++j)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
	}
	for(int i=1;i<N;++i)
		for(int j=1;j<N;++j){
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
			if(j<=i)s[i][j]+=!c[i][j];
		}
	while(t--){
		scanf("%d%d",&n,&m);
		printf("%d\n",s[n][m]);
	}
}

D2T2:先从大到小排序,若q=0,考虑每次把分成的两个数压进两个队列,那么压进去的数一定不大于上次压进对应队列的数。容易验证若q!=0仍然成立。

#include<algorithm>
#include<cstdio>
#include<functional>
using namespace std;
const int M=7e6+5;
int q1[M],q2[M],q3[M];
int m,q,u,v,t;
int a1,b1,a2,b2,a3,b3;
inline int pop(){
	return a1!=b1&&(a2==b2||q1[a1]>=q2[a2])&&(a3==b3||q1[a1]>=q3[a3])?q1[a1++]:a2!=b2&&(a1==b1||q2[a2]>=q1[a1])&&(a3==b3||q2[a2]>=q3[a3])?q2[a2++]:q3[a3++];
}
int main(){
	scanf("%d%d%d%d%d%d",&b1,&m,&q,&u,&v,&t);
	for(int i=0;i<b1;++i)
		scanf("%d",q1+i);
	sort(q1,q1+b1,greater<int>());
	for(int i=1;i<=m;++i){
		int j=pop()+(i-1)*q;
		int k=1ll*j*u/v;
		q2[b2++]=max(k,j-k)-i*q;
		q3[b3++]=min(k,j-k)-i*q;
		if(i%t==0)
			printf(i==t?"%d":" %d",j);
	}
	puts("");
	for(int i=1;i<=b1+m;++i){
		int j=pop()+m*q;
		if(i%t==0)
			printf(i==t?"%d":" %d",j);
	}
}

D2T3:先枚举两只猪,算出对应抛物线能打掉哪些猪。f[S]表示状态为S的最小步数,显然可以o(n^2)转移。考虑到每只猪都要打掉,因此只用考虑某一只没被打掉的猪和其他猪一起被打掉的情况,就可以o(n)转移了。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<utility>
#define x first
#define y second
using std::pair;
typedef double real;
typedef pair<real,real>vec;
const real eps=1e-8;
int in(vec a,vec l){
	return fabs(l.x*a.x*a.x+l.y*a.x-a.y)<eps;
}
const int N=18;
vec a[N];
void eq1(int&a,int b){
	a=b<a?b:a;
}
int q,n,e[N][N],f[1<<N];
int main(){
	scanf("%d",&q);
	while(q--){
		memset(e,0,sizeof e);
		scanf("%d%*d",&n);
		for(int i=0;i<n;++i)
			scanf("%lf%lf",&a[i].x,&a[i].y);
		for(int i=0;i<n;++i){
			e[i][i]=1<<i;
			for(int j=i+1;j<n;++j){
				vec s=a[i],t=a[j];
				real a1=s.x*s.x,b1=s.x,c1=s.y;
				real a2=t.x*t.x,b2=t.x,c2=t.y;
				real d=a1*b2-a2*b1;
				if(fabs(d)>eps){
					real x=(c1*b2-c2*b1)/d;
					if(x<-eps){
						vec l(x,(a1*c2-a2*c1)/d);
						for(int k=0;k<n;++k)
							e[i][j]|=in(a[k],l)<<k;
					}
				}
			}
		}
		for(int i=1;i<1<<n;++i)
			f[i]=n;
		for(int i=0;i<1<<n;++i){
			int j=__builtin_ctz(~i);
			for(int k=j;k<n;++k)
				eq1(f[i|e[j][k]],f[i]+1);
		}
		printf("%d\n",f[(1<<n)-1]);
	}
}

  

时间: 2024-10-09 19:06:59

NOIP2016题解的相关文章

[题解]noip2016普及组题解和心得

[前言] 感觉稍微有些滑稽吧,毕竟每次练的题都是提高组难度的,结果最后的主要任务是普及组抱一个一等奖回来.至于我的分数嘛..还是在你看完题解后写在[后记]里面.废话不多说,开始题解. 第一题可以说的内容不是很多吧.直接暴力,计算每种铅笔需要花费的金额. 只不过计算的时候,需要注意如下问题 如果不是整数倍,除完后要加1 神奇的Linux系统,很多人的第三个点wa了,所以要养成良好的编写代码的习惯 Code(我的源程序) 1 #include<iostream> 2 #include<fst

noip2016十连测题解

以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <string> #include <bitset> #include <vector> #include <

[NOIP2016]换教室 题解(奇怪的三种状态)

2558. [NOIP2016]换教室 [题目描述] 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第i(1<i<n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室ci上课,而另一节课程在教室di进行. 在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的n节安排好的课程.如果学生想更换第i节课程的教室,则需要提出申请.若申请通过,学生就可以在第i个时间段去教室di

【题解】NOIP2016换教室

哇好开心啊!写的时候真的全然对于这个加法没有把握,但还是大着胆子试着写了一下--竟然过了样例?于是又调了一下就过啦. 不过想想也觉得是正确的吧,互相独立的事件对于期望的影响自然也是相互独立的,可以把所有的情况看成一个整体,不同的统计方式只是分组的区别,最后算出来的答案肯定是一样的.dp的状态比较显然:dp[i][j][0/1]代表当前在第i节课,已经用去了j次申请的机会,0/1分别代表当前这一节课是否申请.那么这个时候就分情况讨论,计算这一次的选择对于答案的影响. 这些不同的情况分别是:当前和上

【题解】NOIP2016愤怒的小鸟

一眼n<=18状压dp--方程什么的都很显然,枚举两只小鸟,再将这条抛物线上的小鸟抓出来就好啦.只是这样O(n^3)的dp必然是要TLE的,我一开始这样交上去显然跑得巨慢无比,后来转念一想:面对一个崭新的情况的时候,只有搭配的优劣之分,没有先后的区别,所以最外面的一层可以直接去掉,变成O(n^2)的dp.这样就跑的很快啦~ PS:print()函数只是调试输出,作用是输出now 的二进制形式+dp[now]: #include <bits/stdc++.h> using namespac

天天爱跑步[NOIP2016]

时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一棵包含n个结点和n-1条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si,终点为Ti.每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发,以每秒跑一条边的速度,不间断

[NOIP2016]愤怒的小鸟 D2 T3 状压DP

[NOIP2016]愤怒的小鸟 D2 T3 Description Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如y=ax2+bx的曲线,其中a,b是Kiana指定的参数,且必须满足a<0. 当小鸟落回地面(即x轴)时,它就会瞬间消失. 在游戏的某个关卡里,平面的第一象限中有n只绿色的小猪,其中第i只小猪所在的坐标为(xi,yi). 如果某只小鸟的飞行

NOIP2016天天爱跑步

2557. [NOIP2016]天天爱跑步 时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一棵包含n个结点和n-1条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si,终点为Ti.每天打卡任务开始时,所有玩家在第0秒同时从

[NOIP2016]换教室 D1 T3 Floyed+期望DP

[NOIP2016]换教室 D1 T3 Description 对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第 i ( 1≤ i≤n)个时同段上, 两节内容相同的课程同时在不同的地点进行, 其中, 牛牛预先被安排在教室 ci上课, 而另一节课程在教室 di进行. 在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的n节安排好的课程.如果学生想更换第i节课程的教室,则需要提出中情.若申请通过,学生