test20190815 NOIP2019 模拟题

100+60+40=200,被后面两个题卡着我很不爽。

立方数

【问题描述】
作为 XX 战队的狂热粉丝,MdZzZZ 看到了自己心仪的队伍在半决赛落败,顿时 心灰意冷。看着自己手中的从黄牛那里抢来的天价总决赛门票,MdZzZZ 觉得去鸟 巢已经没有意义了,于是他决定去跳“水立方”。在他准备进“水立方”体育馆 时,一位大妈拦住了他的去路,并产生了一下对话:

大妈:“年轻人我看你印堂发黑,恕我冒昧直言,此去一行怕是会有什么不测。”
MdZzZZ:“大妈别拦我,我要跳水立方发泄一下!”
大妈:“年轻人,做事要三思而后行,你知道这水立方最著名的是什么吗?”
MdZzZZ:“不知...”
大妈:“这水立方最有名的是‘立方数’!”
MdZzZZ:“哦?”
大妈:“别急,听我细细道来。‘立方数’就是,如果一个数可以被写作是一个正整数的 3 次方,则这个数就是立方数。例如 1,8,27 就是最小的 3 个立方 数。”
MdZzZZ:“……”
大妈:“当然,想要在这水立方中来去自如,你需要知道‘立方差数’!”
大妈:“若一个数可以被写作是两个立方数的差,则这个数就是‘立方差数’,例如 7(8-1),26(27-1),19(27-8)都是立方差数。如果你能够判断随便一个数是不是‘立方差数’,那么你就可以真正地在这一片小天地中当一条无忧无虑的小鱼...”

未等 MdZzZZ 反应过来,大妈以飘然远去,留下他一个人在那边细细思索。那么现在你的问题来了,你需要帮助 MdZzZZ 解决这个问题。 现在给定一个数 P,MdZzZZ 想要知道这个数是不是立方差数。

当然你有可能随机输出一些莫名其妙的东西,因此 MdZzZZ 有 T 次询问~这个问题可能太难了…… 因此 MdZzZZ 规定 P 是个质数!

【输入格式】
输入文件名为 cubicp.in。 第一
行一个数 T,表示有 T 组数据。
接下来 T 行,每行一个数 P。

【输出格式】
输出文件名为 cubicp.out。 输出 T 行,对于每个数如果是立方差数,输出“YES”,否则输出“NO”。

【输入输出样例 1】
cubicp.in
5
2
3
5
7
11
cubicp.out
NO
NO
NO
YES
NO

【数据规模与约定】
对于 30%的数据 p<=100。
对于 60%的数据 p<=10^6。
对于 100%的数据 p<=10^12,T<=100。

noip2018模拟赛 立方数2

[p=a^3-b^3=(a-b)(a^2+ab+b^2)\ ecause p in P\ herefore a-b=1,p=a^2+ab+b^2\ herefore a^2+a(a-1)+(a-1)^2=p\ herefore a=frac{3+sqrt{12p-3}}{6} ]

int main(){
	freopen("cubicp.in","r",stdin),freopen("cubicp.out","w",stdout);
	for(int t=read<int>();t--;){
		LL p=read<LL>();
		int a=(3+sqrt(12*p-3))/6;
		puts(p==3LL*a*a-3*a+1?"YES":"NO");
	}
	return 0;
}

动态规划

【问题描述】
MdZzZZ 在悟得人生终极奥义之后,决定去西藏净化一下自己的灵魂。正当他欣赏沿途的风景之时,突然有一大片羊群闯入了他的视线。

这时 MdZzZZ 想到了一 个问题,问题是这样的:假如羊群一共有 n 只羊,把它们排成一条直线,并且给 每个羊标上一个价值。定义一段区间的价值为这段区间相同的数的对数。MdZzZZ 想要将这个羊群分成恰好 k 段区间,之后计算这 n 只羊的价值为这 k 段区间的价值 和。MdZzZZ 现在想让最终这 n 只羊的价值和尽可能少。

例如 6 只羊的价值分别为 1,1,2,2,3,3,现在要将他们切成 3 段。MdZzZZ 立马 想到了一个好方法,分成[1],[1,2],[2,3,3],这样只有第三个区间有 1 的价值。 因此这 6 只羊的价值和为 1。当 MdZzZZ 的思维探索到更多只羊的时候,他不禁陷入 了沉思...然后 MdZzZZ 深深沉迷在思考的世界里面,并且睡着了。

然而美好的时光总是那么短暂,马上天就要黑了。MdZzZZ 必须要在天黑之前找到旅馆并且住下来。你现在需要帮助 MdZzZZ 解决这个问题,并且尽快把他叫醒。当然你要先解决问题再叫醒他,不然 MdZzZZ 可能会带着起床气一直不肯离开。

【输入格式】
输入文件名为 dp.in。
第一行两个数 n,k。
接下来一行 n 个数 ai 表示这 n 个数。

【输出格式】
输出文件名为 dp.out。
一个数表示答案。

【输入输出样例 1】
dp.in
10 2
1 2 1 2 1 2 1 2 1 2
dp.out
8

【数据规模与约定】
对于 30%的数据 n<=10。
对于 60%的数据 n<=1000。
对于 100%的数据 1<=n<=100000,1<=k<=min(n,20),1<=ai<=n。
其中有 30%的数据满足 ai 完全相同均匀分布在所有数据中。

CF868F Yet Another Minimization Problem

先写出DP方程

[f(i,k)=min_{0 leq j < i} f(j,k-1)+w(j+1,i) ]

因为(k)很小,所以用不着二分导数斜率。考虑后面那个(w)函数,它满足四边形不等式。

这个很直观,考虑那个 包含≥交叉 的四条线段的图像,发现你找不出来反例。

尝试证明(forall a<b)

[w(a,b+1) + w(a+1,b) geq w(a,b) + w(a+1,b+1) ]

移项得到

[w(a,b+1)-w(a,b) geq w(a+1,b+1) - w(a+1,b) ]

而下面那个差式相当于([a,b])([a+1,b])向右拓展一格的增量,而这个大小关系很显然。

由于(w)的贡献无法快速计算,所以采用值域分治做法

设当前的求解区间为([l,r]),最优决策区间([L,R])。对于当前分治的中点(mid),我们需要在([L,min(R,mid)])中暴力找到最优决策(k)。注意到从(w(l,r))(w(l,r+1))或者从(w(l,r))(w(l+1,r))都是可以做到(O(1))的,只要开一个桶记录当前区间每个颜色出现次数就可以啦。把指针(i)(L)移到(min(R,mid))并不断的算(f(i)+w(i,mid)),最终可以找到(k)

注意一点,当进入求解区间时,我们的应该要确保([L,l−1])的信息的存在,这样才能保证分治的复杂度。

于是我们考虑进子问题之前如何先处理出子问题的答案。先看左边的子问题(([l,mid−1],[L,k]))显然和当前问题的([L,l−1])是一样的。注意到我们在求(k)的时候对(w)和桶都做了修改,那么我们直接还原回来就可以进左子问题了。

而右子问题呢?(([mid+1,r],[k,R]))它要预处理的是([k,mid]),而当前的是([L,l−1])。所以我们先把右端点指针从(l−1)移到(mid),桶和(w)都加上去,再把左端点从(L)移到(k),桶和(w)都减掉,接着进去就好了。回溯的时候还是要还原到([L,l−1]),因为上一层要接着用。

配上LaTeX画图???

求解区间:(|gets预处理 o| lfrac{qquadqquadqquaddownarrow^{mid}qquadqquadqquad}{}r)
决策区间:(Lfrac{qquadqquadqquadqquaddownarrow^{k}qquadqquadqquad}{}R)

时间复杂度(O(nk log n))


考试的时候还是太naive了,一心想着找出(w)的表达式再来看单调性什么的。没想到可以直接从含义来看……另外我也没有尝试打表。感觉我可能需要掌握更多考试猜想技巧。

co int N=100000+10;
int n,k,a[N],b[N];
LL f[N],g[N];

void solve(int l,int r,int L,int R,LL w){
	if(l>r) return;
	int mid=(l+r)>>1,p=min(mid,R),k=0;
	for(int i=l;i<=mid;++i) w+=b[a[i]]++;
	for(int i=L;i<=p;++i) w-=--b[a[i]],f[mid]>g[i]+w?f[mid]=g[i]+w,k=i:0;
	
	for(int i=l;i<=mid;++i) w-=--b[a[i]];
	for(int i=L;i<=p;++i) w+=b[a[i]]++;
	solve(l,mid-1,L,k,w);
	
	for(int i=L;i<k;++i) w-=--b[a[i]];
	for(int i=l;i<=mid;++i) w+=b[a[i]]++;
	solve(mid+1,r,k,R,w);
	
	for(int i=l;i<=mid;++i) --b[a[i]];
	for(int i=L;i<k;++i) ++b[a[i]];
}
int main(){
	freopen("dp.in","r",stdin),freopen("dp.out","w",stdout);
	read(n),read(k);
	for(int i=1;i<=n;++i)
		g[i]=g[i-1]+b[read(a[i])]++;
	memset(b,0,sizeof b);
	while(k--){
		memset(f,0x3f,sizeof f);
		solve(1,n,1,n,0);
		swap(f,g);
	}
	printf("%lld
",f[n]);
	return 0;
}

游戏

【问题描述】
MdZzZZ 最近十分的苦恼,因为他的好朋友都去玩 Pokemon Go!了,而他向来对这一类的游戏不屑一顾,因此每天都没有人来找他玩。于是 MdZzZZ 自己想了一 个新的游戏,他自豪地将这游戏取名为猜数字游戏。

游戏规则是这样的:一共有 n 个互不相同的正整数,MdZzZZ 可以每次猜一段 区间的最小值。例如对于某段区间[li,ri]一定会有一个最小值。

于是他开心的邀请了他的好友 WdZzZZ 来玩这个游戏。令 MdZzZZ 自豪的是,在这个游戏中,他总能构造出一种方案使得 WdZzZZ 满意,直到……WdZzZZ 自己猜的就是矛盾的!例如 WdZzZZ 猜[1,5]的最小值是 2,[1,6]的最小值是 3,这显然就是矛盾的。

现在你需要帮助 MdZzZZ 判断,在 WdZzZZ 第几次猜数字开始就已经矛盾了。

【输入格式】
输入文件名为 number.in。 第一行两个数 n 和 T,
表示有 n 个数字,LYK 猜了 T 次。
接下来 T 行,每行三个数分别表示 li,ri 和 xi。

【输出格式】
输出文件名为 number.out。 输出一个数表示第几次开始出现矛盾,
如果一直没出现矛盾输出 T+1。

【输入输出样例 1】
number.in
20 4
1 10 7
5 19 7
3 12 8
1 20 1

number.out
3

【数据规模与约定】
对于 50%的数据 n<=8,T<=10。
对于 80%的数据 n<=1000,T<=1000。
对于 100%的数据 1<=n,T<=1000000,1<=li<=ri<=n,1<=xi<=n(但并不保证一开始的所有数都是 1~n 的)。

【Hint】 建议使用读入优化

ChiTongZ的题解

直接说正解,将操作离线,然后二分到哪一个操作就挂了,对于当前区间内的操作,把它排序,按照第三权值从大到小。

考虑哪一些情况是不行的,首先是一个数出现在了两个不相交的区间,其次是权值大的区间覆盖了权值小的区间,然后就没有然后了。

注意第二种情况要取交集,就是所有权值为当前权值的区间的交集。

那么对于第一种情况,直接 lmax, rmin 维护出交集就可以了。if (lmax > rmin) return 挂了;

然后对于第二种情况,标称提供了一种极佳的并查集做法,就是并查集内维护每一个元素 i 延伸最右的点 p。这个是根据每种 x 最后的并集来更新的。

考虑到排序是从大到小的,若当前的交集为 [lmax,rmin] 并且(可以借助标程理解)find(lmax) > rmin

由于当前的权值尚未去更新并查集,所以只有之前的区间覆盖当前区间,而之前的区间权值一定更大,所以情况不合法,属于情况二。

时间复杂度(O(T log T))


大概这道题的关键在于那些讨论,考试的时候我都没有去尝试骗分……没准就推出正解了。然后比较妙的就是按照权值从大到小来做(机房里讨论认为从小到大做),然后就是用并查集来维护并集。

co int N=1000000+10;
int n,T,fa[N];
struct node{int l,r,x;}p[N],e[N];

il bool operator<(co node&a,co node&b){
	return a.x>b.x;
}
int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}

bool check(int k){
	for(int i=1;i<=n+1;++i) fa[i]=i;
	copy(e+1,e+k+1,p+1),sort(p+1,p+k+1);
	int lmax=p[1].l,lmin=p[1].l,rmax=p[1].r,rmin=p[1].r;
	for(int i=2;i<=k;++i){
		if(p[i].x<p[i-1].x){
			if(find(lmax)>rmin) return 0; // 交集被覆盖 
			for(int j=find(lmin);j<=rmax;++j) // 并集合并
				fa[find(j)]=find(rmax+1);
			lmin=lmax=p[i].l,rmin=rmax=p[i].r;
		}
		else{
			lmin=min(lmin,p[i].l),lmax=max(lmax,p[i].l);
			rmin=min(rmin,p[i].r),rmax=max(rmax,p[i].r);
			if(find(lmax)>rmin) return 0;
		}
	}
	if(find(lmax)>rmin) return 0;
	return 1;
}
int main(){
	freopen("number.in","r",stdin),freopen("number.out","w",stdout);
	read(n),read(T);
	for(int i=1;i<=T;++i) read(e[i].l),read(e[i].r),read(e[i].x);
	int l=1,r=T;
	while(l<r){
		int mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	printf("%d
",l+1);
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/test20190815.html