【题解】2021.8.28 - zhengru IOI 七连测 Day1

T1 数列

思路

  • 没什么,按题意模拟。

  • (似乎有种压缩算法也是这么写的QWQ?)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,lst,cnt,tot,nowa;
int ans[2333],uus[2333];
int main(){
	scanf("%d",&n);
	tot=1;ans[1]=1;
	n--;
	while(n--){
		lst=ans[1];cnt=1;nowa=0;
		if(tot==1) uus[1]=1,uus[2]=lst,nowa=2;
		else{
			for(register int i=2;i<=tot;++i){
				if(ans[i]==lst) cnt++;
				else{
					uus[++nowa]=cnt;
					uus[++nowa]=lst;
					lst=ans[i];
					cnt=1;
				}
			}
			uus[++nowa]=cnt;
			uus[++nowa]=lst;
			
		}
		for(register int i=1;i<=nowa;++i){
			ans[i]=uus[i];
		}
		tot=nowa;
	}
	for(register int i=1;i<=tot;++i){
		printf("%d",ans[i]);
	}
	printf("\n");
	return 0;
} 

T2 索引

思路

  • (我个究极智障白吃罚时 \(+\) 白丢 \(30\) ,无语至极)

  • (我个究极智障没看到修改完以后的 \(A_i\) 也保证严格递增,无语至极)

  • 由上行可知,数组 \(A\) 的增速比其下标的增速快,反过来也一样。

  • 综上所述,当 \(A_i<i\) 时, \(i\) 前面不可能满足答案。把这句话的小于换成大于、前面换成后面以后,它仍然成立。

  • 根据简单的大小关系判断答案位置,这就是二分!

  • 可是,这只是一次查询。

  • 不仅如此,修改怎么办呢?

  • 事实上,我们只管 \(A_i\) ,正确性也是可以保障的。原因看前面。

  • 于是对于每个位置,我们选择遍历 \(1\sim k_{now}\) ,然后再判断。

  • 总复杂度:\(O(k^2\log n)\),可以通过。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int num[10002333],op[2333][5];
//                    ↑L、R、C 
int n,k,c,l,r;
bool fnd;
inline void chk(){
	for(register int j=1;j<=n;++j)
		if(num[j]==j){
			printf("YES\n");
			return;
		}
	printf("NO\n");
	return;
} 
inline int get_num(int pos,int kkk){
	int stt=num[pos];
	for(register int i=1;i<=kkk;++i)
		stt=stt+(((op[i][1]<=pos)&&(op[i][2]>=pos))?op[i][3]:0);
	return stt;
}
int main(){
	scanf("%d%d",&k,&n);
	for(register int i=1;i<=n;++i)
		scanf("%d",&num[i]);
	num[0]=num[n+1]=num[n+2]=num[n+3]=num[n+4]=num[n+5]=-1919810;
	chk();
	for(register int i=1;i<k;++i){
		scanf("%d%d%d",&l,&r,&c);
		fnd=false;
		op[i][1]=l,op[i][2]=r,op[i][3]=c;
		int L=0,R=n+1;
		while(L<=R){
			int M=(L+R)>>1;
			int gen=get_num(M,i);
//			printf("%d %d\n",M,gen);
			if(gen<M) L=M+1;
			else if(gen>M) R=M-1;
			else{
				fnd=true;
				break;
			}
		}
//		for(register int j=1;j<=n;++j){
//			printf("%d ",get_num(j,i));
//		}printf("\n");
		if(fnd) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

T3 奇数

思路

  • 时间关系,这里直接引出结论:莫队!

  • 所谓莫队,就是把操作离线。

  • 离散化后以查询左端点所在区间为第一关键字,右端点为第二关键字进行排序,然后维护两个指针 \(l\)\(r\) ,直接暴力覆盖即可。

  • 正确性:我们统计答案时选择,一个数出现偶数次时将答案减一,反之加一,这样 \(l\)\(r\) 重复覆盖的位置对答案的贡献为 \(0\) ,所以这么覆盖正确。

  • 至于复杂度,已经排序了,就省去了大量无用的覆盖,复杂度可以保证。

  • 补充一点,分块时,我们选择分 \(\sqrt Q\) 个区间,然后可以用除法算出某个点所在的区间编号,随后便可以排序了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
int n,q,nol,nor,num[100233],cop[100233],ans[100233],now,sqn;
struct modui{
	int l,r,pos;
}opt[100233]; 
int mp[233333];
inline bool cmp(modui a,modui b){
	if(a.l/sqn!=b.l/sqn) return a.l/sqn<b.l/sqn;
	else return a.r<b.r;
}
int main(){
	scanf("%d",&n);
	for(register int i=1;i<=n;++i){
		scanf("%d",&num[i]);
		cop[i]=num[i];
	}
	sort(cop+1,cop+n+1);
	for(register int i=1;i<=n;++i){
		num[i]=lower_bound(cop+1,cop+n+1,num[i])-cop;//与其说是找大于等于的,不如说是找等于的,实现去重且离散 
	}
	scanf("%d",&q);
	sqn=sqrt(q);
	for(register int i=1;i<=q;++i){
		int L,R;
		scanf("%d%d",&L,&R);
		opt[i].l=L,opt[i].r=R,opt[i].pos=i;
	}
	sort(opt+1,opt+q+1,cmp);
	mp[num[1]]++;now=nol=nor=1;
	for(register int i=1;i<=q;++i){
		while(nol>opt[i].l) now+=(((++mp[num[--nol]])&1)?1:-1);
		while(nor<opt[i].r) now+=(((++mp[num[++nor]])&1)?1:-1);
		while(nol<opt[i].l) now+=(((++mp[num[nol++]])&1)?1:-1);
		while(nor>opt[i].r) now+=(((++mp[num[nor--]])&1)?1:-1);
		ans[opt[i].pos]=now;
	}
	for(register int i=1;i<=q;++i){
		printf("%d\n",ans[i]);
	}
	return 0;
} 

T4 解谜

思路

  • \(f[n]\) 表示由 \(n\) 个点组成的二叉树的个数, \(g[n]\) 表示这所有方案中的叶子总数,则答案为 \(\frac{g[n]}{f[n]}\)

  • 那么,对于 \(n-1\) 个点构成的二叉树来讲,它原本是有 \(2n-2\) 个给儿子的空位的(每个点两个),而根节点不占用空位,所以其只占用了 \(n-2\) 个,还剩下 \(n\) 个,而这 \(n\) 个位置中每挑一个位置放叶子就产生了一种新的方案,共 \(n\) 种,所以对于每种方案的叶子数都乘了一个 \(n\) ,由此易得 \(g[n]=f[n-1] \times n\) ,这样,答案就变成了 \(\frac{f[n-1] \times n}{f[n]}\)

  • 而由于 \(f[n]\) 是卡特兰数(百度优先搜索),将其通项公式带入,再进行化简,可以得到 \(\frac{n(n+1)}{2(2n-1)}\) ,直接计算即可。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const ll MO(2148473647);ll n;
inline ll fpow(ll x,ll y){
	ll ans=1;
	while(y){
		if(y&1) ans=(ans*x)%MO;
		x=(x*x)%MO;
		y=y>>1;
	}
	return ans%MO;
} 
int main(){
	scanf("%lld",&n);
	printf("%lld\n",(n*((n+1)%MO)%MO)*fpow(2*((n+n)%MO-1)%MO,MO-2)%MO);
	return 0;
}
posted @ 2021-10-06 16:04  Binaries  阅读(27)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end