20190902模拟赛

T1:斐波那契( f i b o n a c c i fibonacci fibonacci)
【 题目描述】
小 C 养了一些很可爱的兔子。
有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行
繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子。我们假定,
在整个过程中兔子不会出现任何意外。
小 C 把兔子按出生顺序,把兔子们从 1 开始标号,并且小 C 的兔子都是 1 号兔子和 1
号兔子的后代。 如果某两对兔子是同时出生的,那么小 C 会将父母标号更小的一对优先标
号。

其中,一个箭头 A → B 表示 A 是 B 的祖先,相同的颜色表示同一个月出生的兔子。
为了更细致地了解兔子们是如何繁衍的, 小 C 找来了一些兔子,并且向你提出了m个
问题: 她想知道关于每两对兔子 a i a_i ai b i b_i bi, 他们的最近公共祖先是谁。你能帮帮小 C 吗?
一对兔子的祖先是这对兔子以及他们父母(如果有的话) 的祖先, 而最近公共祖先是指
两对兔子所共有的祖先中,离他们的距离之和最近的一对兔子。 比如, 5 和 7 的最近公共祖
先是 2, 1 和 2 的最近公共祖先是 1, 6 和 6 的最近公共祖先是 6。

m &lt; = 3 e 5 , a , b &lt; = 1 e 12 m&lt;=3e5,a,b&lt;=1e12 m<=3e5,a,b<=1e12

随便打表发现一个点 x x x的父亲是 x − x- x小于 x x x的最大斐波那契数。

第一次打输出优化然后发现自己写的 i n t int int挂了 20 20 20,果然只能计算小数据情况的对拍并不是万能的,反而还会误导人。

#include<bits/stdc++.h>
using namespace std;

#define LL long long
LL f[2000];

char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>void read(T &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

void put(LL a){
	if(!a){
		putchar('0');
		return;
	}
	static int q[22]={};
	for(;a;a/=10) q[++q[0]] = a%10;
	for(;q[0];) putchar(q[q[0]--]+'0');
}


int main(){
	freopen("fibonacci.in","r",stdin);
	freopen("fibonacci.out","w",stdout);
	f[1] = f[0] = 1;
	for(int i=2;f[i-1]<=1e12;i++)
		f[i] = f[i-1] + f[i-2];
	int m;read(m);
	for(;m--;){
		LL a,b;
		int c1=59,c2=59;
		read(a),read(b);
		for(;a!=b;){
			if(a<b)swap(a,b),swap(c1,c2);
			for(;c1 >= 0 && f[c1] >= a;c1--);
			a -= f[c1] , c1--;
		}
		put(a);puts("");
	}
}

T2:
数颜色(color)
【 题目描述】
小 C 的兔子不是雪白的,而是五彩缤纷的。 每只兔子都有一种颜色, 不同的兔子可能有
相同的颜色。小 C 把她标号从 1 到 n 的 n 只兔子排成长长的一排,来给他们喂胡萝卜吃。
排列完成后,第 i 只兔子的颜色是 ai 。
俗话说得好,“萝卜青菜,各有所爱”。 小 C 发现,不同颜色的兔子可能有对胡萝卜的
不同偏好。 比如, 银色的兔子最喜欢吃金色的胡萝卜, 金色的兔子更喜欢吃胡萝卜叶子, 而
绿色的兔子却喜欢吃酸一点的胡萝卜……为了满足兔子们的要求,小 C 十分苦恼。 所以, 为
了使得胡萝卜喂得更加准确,小 C 想知道在区间 [li, ri] 里有多少只颜色为 ci 的兔子。
不过, 因为小 C 的兔子们都十分地活跃, 它们不是很愿意待在一个固定的位置;与此同
时,小 C 也在根据她知道的信息来给兔子们调整位置。 所以,有时编号为 xj 和 xj+1 的两
只兔子会交换位置。
小 C 被这一系列麻烦事给难住了。你能帮帮她吗?
n &lt; = 3 e 5 n&lt;=3e5 n<=3e5

动态开点线段树裸题。
表示15分钟打完正解+对拍的我忍不住被数组越界的诱惑就开了 150 ∗ 3 e 5 150 * 3e5 1503e5的空间。
可以用vector+二分就不卡空间。

AC Code:

#include<bits/stdc++.h>
#define maxn 300005
#define maxpt maxn * 60
using namespace std;

int n,m,a[maxn];
int rt[maxn],lc[maxpt],rc[maxpt],siz[maxpt],tot;

void Insert(int &now,int l,int r,int pos,int v){
	if(!now){
		siz[++tot] = siz[now] , lc[tot] = lc[now] , rc[tot] = rc[now];
		now = tot;
	}
	siz[now] += v;
	if(l==r) return;
	int mid = (l+r) >> 1;
	if(pos <= mid) Insert(lc[now],l,mid,pos,v);
	else Insert(rc[now],mid+1,r,pos,v);
}

int Query(int now,int l,int r,int ql,int qr){
	if(!now || !siz[now] || l>qr || ql>r) return 0;
	if(ql <= l && r <= qr) return siz[now];
	int mid = (l+r) >> 1;
	return Query(lc[now],l,mid,ql,qr) + Query(rc[now],mid+1,r,ql,qr);
}

char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>void read(T &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

void put(int a){
	if(!a){
		putchar('0');
		return;
	}
	static int q[22]={};
	for(;a;a/=10) q[++q[0]] = a%10;
	for(;q[0];) putchar(q[q[0]--]+'0');
}

int main(){
	
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	
	read(n),read(m);
	for(int i=1;i<=n;i++){
		read(a[i]);
		Insert(rt[a[i]],1,n,i,1);
	}
	for(int i=1;i<=m;i++){
		int op,l,r,c;
		read(op);
		if(op == 1){
			read(l),read(r),read(c);
			put(Query(rt[c],1,n,l,r));
			putchar('\n');
		}
		else{
			read(l);
			Insert(rt[a[l]],1,n,l,-1);
			Insert(rt[a[l+1]],1,n,l+1,-1);
			swap(a[l],a[l+1]);
			Insert(rt[a[l]],1,n,l,1);
			Insert(rt[a[l+1]],1,n,l+1,1);
		}
	}
}

T3:
分组(division)
【题目描述】
小 C 在了解了她所需要的信息之后,让兔子们调整到了恰当的位置。 小 C 准备给兔子
们分成若干个小组来喂恰当的胡萝卜给兔子们吃。
此时, n 只兔子按一定顺序排成一排,第 i 只兔子的颜色是 ai 。 由于顺序已经是被
调整好了的,所以每个小组都应当是序列上连续的一段。
在分组前, 小 C 发现了一个规律:有些兔子会两两发生矛盾。 并且,两只兔子会发生矛
盾,当且仅当代表他们的颜色的数值之和为一个正整数的平方。 比如, 1 色兔子和 2 色兔子
不会发生矛盾,因为 3 不是任何一个正整数的平方; 而 1 色兔子却会和 3 色兔子发生矛盾,
因为 4 = 22.
小 C 认为,只要一个小组内的矛盾不要过大就行。因此,小 C 定义了一个小组的矛盾
值 k ,表示在这个小组里,至少需要将这个组再一次分成 k个小团体; 每个小团体并不需
要是序列上连续的一段,但是需要使得每个小团体内任意两只兔子之间都不会发生矛盾。
小 C 要求, 矛盾值最大的小组的矛盾值 k 不超过 K 就可以了。 当然, 这样的分组方
法可能会有很多个; 为了使得分组变得更加和谐,小 C 想知道,在保证分组数量最少的情况
下, 字典序最小的方案是什么。你能帮帮她吗?
字典序最小的方案是指,按顺序排列分组的间隔位置,即所有存在兔子 i 和 i + 1 在
不同组的位置 i, 和其它所有相同分组组数相同的可行方案相比总有第一个不同的位置比其
它方案小的方案。

K &lt; = 2 , n &lt; = 1 e 5 K&lt;=2,n&lt;=1e5 K<=2,n<=1e5

从后到前贪心能加入就加入即可满足组数最小并且字典序最小。
K = 1 K=1 K=1就矛盾判重, K = 2 K=2 K=2就矛盾判二分图。
因为每个点只有 O ( n ) O(\sqrt n) O(n )个出边, K = 1 K=1 K=1直接判, K = 2 K=2 K=2用带权并查集动态加入判二分图。
复杂度 O ( n n log ⁡ n ) O(n\sqrt n\log n) O(nn logn)
注意判二分图时对于自环需要特别判断,居然(果然)因为这个东西卡了我52分,(一知半解的)正解时没有出路的,暴力才能RK1,对不起打扰了,果然不能支持大数据对拍的对拍的局限性太大了,应该听从自己的内心,思考比对拍好。

AC Code:

#include<bits/stdc++.h>
#define maxn 140000
using namespace std;

int n,K,a[maxn],cnt[maxn];
vector<int>G[maxn];
vector<int>ans;

int F[maxn],H[maxn];
int Find(int now){
	if(!F[now]) return now;
	int t = F[now];
	F[now] = Find(F[now]);
	H[now] ^= H[t];
	return F[now];
}

int main(){
	
	freopen("division.in","r",stdin);
	freopen("division.out","w",stdout);
	
	scanf("%d%d",&n,&K);
	for(int j=1;j<=131072;j++)
		for(int i=1;i*i<=j+131072;i++)
			if(i*i > j)
				G[j].push_back(i*i-j);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	if(K == 1){
		for(int i=n,j;i>=1;i=j){
			for(j=i;j>=1;j--){
				bool flg = 0;
				for(int u=0;u<G[a[j]].size();u++)
					if(cnt[G[a[j]][u]])
						flg = 1;
				cnt[a[j]] ++;
				if(flg) break;
			}
			if(j) ans.push_back(j);
			for(int k=j;k<=i;k++) cnt[a[k]] = 0;
		}
	}
	else{
		for(int i=n,j;i>=1;i=j){
			for(j=i;j>=1;j--){
				bool flg = 0;
				for(int u=0;u<G[a[j]].size();u++)
					if(cnt[G[a[j]][u]]){
						if(a[j] == G[a[j]][u]){
							if(cnt[a[j]] >= 2)
								flg = 1;
							else{
								for(int p=0;p<G[a[j]].size();p++)
									if(cnt[G[a[j]][p]] && G[a[j]][p] != a[j])
										flg = 1;
							}
							continue;
						}
						int x = Find(a[j]) , y = Find(G[a[j]][u]);
						if(x != y){
							F[x] = y;
							H[x] = H[a[j]]^H[G[a[j]][u]]^1;
						}
						else{
							if(H[a[j]] == H[G[a[j]][u]])
								flg = 1;
						}
					}
				cnt[a[j]]++;
				if(flg) break;
			}
			if(j) ans.push_back(j);
			for(int k=j;k<=i;k++) cnt[a[k]] = 0 , F[a[k]] = 0 , H[a[k]] = 0;
		}
	}
	printf("%d\n",ans.size()+1);
	bool fg = 0;
	for(int i=ans.size()-1;i>=0;i--){
		if(!fg) fg = 1; 
		else printf(" ");
		printf("%d",ans[i]);
	}
	puts("");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值