jzoj2020.06.06【NOIP提高组】模拟

本文详细解析了NOIP提高组模拟赛中的三道题目:Bovine Shuffle是寻找不变的奶牛位置数量;Milk Measurement要求计算调整产奶量最高奶牛照片的次数;Barn Painting是树形DP解决不同相邻节点颜色的染色方案数。文章提供了每道题目的描述、输入输出格式、样例及解题思路。

好久没打博客了~~~~~
趁着改完题就搞一搞(话说最近的题都好淼

T1 The Bovine Shuffle

Description

FJ 坚信快乐的牛可以产出更多的牛奶,因此 FJ 在牛棚里安装了一个巨大的迪斯科球并且打算让奶牛们学会跳舞。
FJ在许多出名的奶牛舞中选择了一种叫做 Bovine Shuffle 的舞蹈。这种舞蹈由 FJ 的 N 头奶牛组成。N头奶牛以一种顺序排成一行,接着表演数次 shuffle。每次的 shuffle 会将奶牛重新排列。FJ 为了让奶牛们更加快乐,让奶牛们更容易找到重新排列后的位置,他标记了 N 头奶牛的位置。在最开始,所有奶牛排成一排,第一头奶牛会在位置 1 上,第二只在 2上,以此类推。
我们用 N 个正整数 a1,a2,…,an来描述每次的 shuffle。ai说明了在位置 i上的奶牛在经过这回合的 shuffle 之后,会跑到位置 ai上。令 FJ 倍感非洲的是,即使 i与j不同,ai也可能会等于 aj!所以可能在一次 shuffle 后,有多头奶牛会跑到同一位置上,在这之后这群奶牛也会一同行动。
作为一名资深的养牛大户&坑牛专家的 FJ 猛然发现,无论经过多少次的 shuffle,一直都有 k个位置上有奶牛。FJ现在要你在 1秒内帮他得出 kk 的值,否则就赏你 10^18 mod10 大板!

Input

第一行包含一个整数,N
第二行包含 NN个整数,描述题目中的a 1 ​ ,a 2 ​ …a n ​

Output

一个整数,代表k

Sample Input

4
3 2 1 3

Sample Output

3

赛时

看到第一眼就是找规律,之后算了算感觉是找环,打了5分钟秒掉,然后赛后10分,都没有发现样例都没过。。。。。。(关键是好多大佬也是这么写的)

思路

感觉很多种方法,赛后问了问身旁比赛时腐败的大佬,大概一个这样的思路
对于每个没有被指向的点必然最后一直走不了,样例的3,2,1,3中4没有被指向,所以一定不行。然后再把关于那些不行的点指向的边删去,再判断剩下的点有没有被指向,一直做到那些没有边连向它的点没有。额。。其实有点像拓扑。
关于复杂度,n条边怎么暴力都没事吧?
附上超级丑的代码:

#include<cstdio>
#include<cstring>
#define N 100007 
using namespace std;
int a[N],b[N][500],n,ans,now;
bool bz[N];
void dg(int x){
	bool bz1=false;
	for(int i=1;i<=b[x][0];i++){
		if(bz[b[x][i]]==1){bz1=true;break;}
	}
	if(!bz1) ans--,bz[x]=0,dg(a[x]);
}
int main(){
	freopen("shuffle.in","r",stdin);
	freopen("shuffle.out","w",stdout);
	memset(bz,1,sizeof(bz));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),b[a[i]][++b[a[i]][0]]=i;
	ans=n;
	for(int i=1;i<=n;i++)
		if(!b[i][0]){
			bz[i]=0;
			ans--;
			dg(a[i]);
		}
	printf("%d",ans);
}

因为是改出来的,所以嘛有些地方写的很累赘就交了。

T2 Milk Measurement

Description

最初,农夫约翰的每头奶牛每天生产G加仑的牛奶(1≤G≤10^9)。由于随着时间的推移,奶牛的产奶量可能会发生变化,农夫约翰决定定期对奶牛的产奶量进行测量,并将其记录在日志中。
他的日志中的记录如下:
35 1234 -2
14 2345 +3
第一个条目表明:在第35天,1234号奶牛的产奶量比上次测量时降低了2加仑。
第二个条目表明:在第14天,2345号奶牛的产奶量比上次测量时增加了3加仑。
农夫约翰只有在任何一天内做最多一次测量的时间(即每天最多做一次测量,但可能不做)。不幸的是,约翰有点杂乱无章,他不一定按照时间顺序记下测量结果。为了保持奶牛的产奶动力,农夫约翰自豪地在谷仓的墙上展示了目前产奶量最高的奶牛的照片(如果有若干头奶牛的产奶量最高,他就会展示所有的图片)。

请求出约翰需要调整所展示的照片的次数。

请注意,农夫约翰有一大群奶牛。所以尽管日志中记录了一些奶牛改变了产奶量,但仍然还有很多奶牛的产奶量保持在G加仑。

Input

第一行是两个整数N和G,分别表示测量的次数和初始产奶量。
接下来N行,每行为一次测量。每行三个数:分别表示日期一(在整数1…106范围内),奶牛的编号(在整数1…109范围内),该奶牛的产奶量变化值(一个非负数)。无论如何,每头奶牛的产奶量永远保证在0…10^9范围内。

Output

请输出约翰总共调整所展示的照片的次数。

Sample Input

4 10
7 3 +3
4 2 -1
9 3 -1
1 1 +2

Sample Output

3

思路

赛时10分,思路欠缺,细节漏掉。
总体线段树维护最大值,最大值的个数,哪头奶牛是最大值,也就是最大值的位置(这个是来判断最大值个数为1的,所以最大值个数大于1就随便找一个)然后说说怎么用:
当上一次最大值的个数和现在最大值的个数不一样,ans必定加1
当上次和现在的最大值的个数都是1,判断是不是最大值的那一头奶牛自己加了,这个时候就不用+ans,如果是别的奶牛超过他了,也就是两次的最大值的位置不一样再ans++
之后还有一个细节,就是说当所有被登记奶牛的值都小于一开始的值,不代表最多的在被登记的奶牛里面,因为还有很多没有被登记的奶牛。那么处理这种情况,线段树的右边界+1就好了。
线段树没什么技术含量,但是注意,每头奶牛的编号都是int范围内,所以要离散化,最简单粗暴的就是用map喽(stl库大法好
这个代码更丑,读入懒得搞快读,凑合着看吧

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#define N 100007
using namespace std;
map<int,bool>m;
map<int,int>num;
struct node{
	int d,n,s;
}c[N];
int mx[4*N],size[4*N],h[4*N];//mx维护最大,size维护个数,h维护位置
int n,s,cnt,maxn,siz,ans;
bool cmp(node a,node b){return a.d<b.d;}

void get(int now){//维护三个值
	if(mx[now<<1]>mx[now<<1|1]){
		mx[now]=mx[now<<1];
		size[now]=size[now<<1];
		h[now]=h[now<<1];
	}else if(mx[now<<1]<mx[now<<1|1]){
		mx[now]=mx[now<<1|1];
		size[now]=size[now<<1|1];
		h[now]=h[now<<1|1];
	}else{
		mx[now]=mx[now<<1];
		h[now]=h[now<<1];
		size[now]=size[now<<1]+size[now<<1|1];
	}
}

void build(int now,int l,int r){
	if(l==r){
		mx[now]=s;
		size[now]=1;
		h[now]=l;
		return;
	}
	int mid=l+r>>1;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	get(now);
}

void change(int now,int l,int r,int k,int sum){
	if(l==r){
		mx[now]+=sum;
		return;
	}
	int mid=l+r>>1;
	if(k<=mid) change(now<<1,l,mid,k,sum);
	else change(now<<1|1,mid+1,r,k,sum);
	get(now);
}

int main(){
	freopen("measurement.in","r",stdin);
	freopen("measurement.out","w",stdout);
	scanf("%d%d",&n,&s);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&c[i].d,&c[i].n);getchar();
		char g=getchar();scanf("%d",&c[i].s);
		if(g=='-') c[i].s*=-1;
		if(!m[c[i].n]){
			m[c[i].n]=1;
			num[c[i].n]=++cnt; 
		}
		c[i].n=num[c[i].n];
	} //超级丑的读入
	cnt++;//右边界加1,为了维护当所以被登记的牛的值小于初始值
	build(1,1,cnt);//建树
	sort(c+1,c+n+1,cmp);//按天数快排
	maxn=s;siz=cnt;
	int g=1;
	for(int i=1;i<=n;i++){
		change(1,1,cnt,c[i].n,c[i].s);
		if(maxn!=mx[1]||siz!=size[1]){//当最大值和个数变化导致有可能换照片
			ans++;//先加一下
			if(siz==size[1]&&g==h[1]) ans--;//只有一个奶牛并且奶牛同一个,说明不用换,ans捡回来
		}
		maxn=mx[1],siz=size[1],g=h[1];//赋上新的值
	}
	printf("%d\n",ans);
}

线段树也挺裸的,就是恶心的地方多多

T3 Barn Painting(又名大淼题,你配T3)

Description

给定一颗N个节点组成的树,3种颜色,其中K个节点已染色,要求任意两相邻节点颜色不同,求合法染色方案数。

Sample Input

4 1
1 2
1 3
1 4
4 3

Sample Output

8

吐槽一下中文翻译,十几行的英文就一句话,证明中华文化博大精深的也挺好的,但是modulo 10^9+7这个你不翻译不就是害人吗?
咳咳,所以要mod啊

思路

本来最后几分钟搞出来的想骗骗分,结果T1 T2是骗分的,这题当场a了。。。。。。
真的什么水题嘛,这么裸的树型dp,我都不想解释了,直接看code吧

#include<cstdio>
#define N 100007
using namespace std;
struct node{
	int to,nxt;
}e[N<<1];
int n,k,cnt,head[N<<1];
long long f[N][4];//1,2,3分别三种颜色
long long p=1000000007;//mod的值
bool bz[N];
void add(int u,int v){//链式前向星
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

void dfs(int x,int fa){
	for(int i=head[x];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,x);
		(f[x][1]*=(f[v][2]+f[v][3]))%=p;
		(f[x][2]*=(f[v][1]+f[v][3]))%=p;
		(f[x][3]*=(f[v][1]+f[v][2]))%=p;//这个dp有多水不用说了吧???
	}
}

int main(){
	freopen("barnpainting.in","r",stdin);
	freopen("barnpainting.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	for(int i=1;i<=k;i++){
		int u,c;
		scanf("%d%d",&u,&c);
		f[u][c]=1;//如果是已知的颜色,那么除了一个给的颜色是1,其他都是0
		bz[u]=true;
	}
	for(int i=1;i<=n;i++)
		if(!bz[i]){//没有已知的初始化都是1
			f[i][1]=1;
			f[i][2]=1;
			f[i][3]=1;
		}
	dfs(1,1);
	long long ans=f[1][1]+f[1][2]+f[1][3];
	ans%=p;
	printf("%lld",ans);
}

考试时叫了3次,前两次差点没开longlong见祖先(但是这并不能说明这题有多hard)

所以总分120,第5。。。。。。虽然是第一个a出来的,但是不爽啊,还是细节问题啊。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值