HGOI-国庆七连测-day1

题解

今天这个题可以说是很悬了,第一题花了两个小时,还只是交了个暴力orz,有点硬核…

在这里插入图片描述


第一题——bread

【题目描述】

  • 区间内n个无色积木进行m次染色,每次将 [ ( i ∗ p + q ) m o d [(i*p+q)mod [(ip+q)mod n + 1 , ( i ∗ q + p ) m o d n + 1,(i*q+p)mod n+1,(iq+p)mod n + 1 ] n +1] n+1]区间内染上i,原有颜色会被覆盖,求最终颜色。

  • 其实我真的交的是暴力,后来经过xyc大佬一波指点才知道可以用并查集来搞一把。
  • 首先可以看出来在染色的时候区间是周期性变化的…所以你只要在m次染色当中做最后n次就可以了。
  • 然后我就打了个裸暴力还骗了50orz
  • 其实可以利用并查集,将已经染过颜色的点并到一个集合当中,这样子你染色的时候在区间当中进行扫描可以直接跳过当中染过颜色的点,由于每次染色并集合最多需要m次,那么复杂度就是 O ( n + m ) O(n+m) O(n+m)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=11000000;
inline void fff(){
	freopen("bread.in","r",stdin);
	freopen("bread.out","w",stdout);
}
int n,m,p,q;
int color[N],top=0;
int f[N];
int find_(int x){
	if(f[x]!=x) f[x]=find_(f[x]);
	return f[x];
}
int main(){
//	fff();
	scanf("%d%d%d%d",&n,&m,&p,&q);
	p%=n;
	q%=n;
//	memset(color,0,sizeof(color));
	for(int i=1;i<=n+1;i++) f[i]=i;
	for(int i=m;i>=1;i--){
		if(i<=m-n) break;
		int l=(i*p+q)%n+1,r=(i*q+p)%n+1;
		if(l>r) swap(l,r);
		l=find_(l);
		while(l<=r){
			color[l]=i;
			int t=find_(l+1);
			f[l]=t;
			l=t;
		}
		
	}
	for(int i=1;i<=n;i++)printf("%d\n",color[i]);
	return 0;
}


第二题——divide

【题目描述】

  • 有价格为1-6的弹珠各a[i]个,总价不超过20k,问弹珠价值能否对半分。
  • 大数据可能有5k组数据

  • 小数据可以用多重背包转01背包水过,但是大数据真的没有办法orz。只能讲讲std了orz
  • 你可以认为在取第i种物品的时候,总价格j是由j-i当中转移过来,条件就是能够有剩下的i让你拿。
  • 然后你会发现这个判断就不好写了。std同学就用了一个玄之又玄的算法。记 f [ i ] [ j ] f[i][j] f[i][j]表示取第i种的时候总价值为j时,i剩下的个数。
  • 那么就有方程
			if(f[i-1][j]>=0) f[i][j]=a[i];
				else if(i<=j) f[i][j]=f[i][j-i]-1;else f[i][j]=-1;
  • 然后你会发现当前状态是从上一层状态转移过来的,那就可以省去一维空间了orz
			if(f[j]>=0) f[j]=a[i];
				else if(i<=j) f[j]=f[j-i]-1;else f[j]=-1;

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
inline void fff(){
	freopen("divide.in","r",stdin);
	freopen("divide.out","w",stdout);
}
int a[7];
int num[210000],siz=0;
int f[210000];
int main(){
//	fff();
	int T;
	scanf("%d",&T);
	while(T--){
		memset(num,0,sizeof(num));
		int sum=0;
		for(int i=1;i<=6;i++){
			scanf("%d",&a[i]);
			sum+=i*a[i];
		}
		if(sum&1){
			printf("Can't be divided.\n");
			continue;
		}
		siz=0;
		sum>>=1;
		memset(f,-1,sizeof(f));
		f[0]=0;
		for(int i=1;i<=6;i++){
			for(int j=0;j<=sum;j++)
				if(f[j]>=0) f[j]=a[i];
				else if(i<=j) f[j]=f[j-i]-1;else f[j]=-1;
		}
		if(f[sum]>=0) printf("Can be divided.\n");
		else printf("Can't be divided.\n");
	}
	return 0;
}

第三题——cmi

【题目描述】

  • 有一全排列,每次可以移动一个数,求排序所需最少移动次数。

  • 其实题目没说清楚,应该是升序排序orz
  • 其实就是求一遍最长上升子序列+ O ( n l o g n ) O(nlogn) O(nlogn)优化
  • 没了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
inline void fff(){
	freopen("cmi.in","r",stdin);
	freopen("cmi.out","w",stdout);
}
const int N=200010;
int n;
int a[200010],ffmax[200010];
int main(){
//	fff();
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int len=0;
	memset(ffmax,0x3f3f,sizeof(ffmax));
	for(int i=1;i<=n;i++){
		int pos=lower_bound(ffmax+1,ffmax+len+1,a[i])-ffmax;
		ffmax[pos]=min(a[i],ffmax[pos]);
		len=max(len,pos);
	}
	int ans=n-len;
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值