2022.9.22———HZOI【CSP-S模拟9】YO寄

Preface

点击查看代码

AtCoder的题好像是

今天T3随只因化暗藏玄只因)爆零了,赛后很难受,总共只得到了19分的好成绩

Rank38/42

19pts+0pts+0pts+0pts=19pts

下午讲题发现意外的听懂了,并且发觉怎么这么水,我真是个shab

当然Sakura讲的没听懂(

T1 最长上升子序列, T2 独特序列, T3 最大GCD, T4连续子段

T1 

一道shab构造题

首先把不是a[i]的扔到队列里,注意是升序

然后从1K1贪心地使字典序最小,当然要先加入a[i]再贪心,为了使得a[]就是最后的LIS,要满足要加入的一个q[L]小于a[i]。因为如果大于的话如果他比a[i+1]要小,那么就使得LIS更长了,如果比a[i+1]要大,那么显然不满足字典序最小的要求。不存在等于的情况(废话)。

最后要对a[K]进行一下特殊判断,因为a[K]并不一定等于n,这也是我赛事shab挂成19pts的原因。

  • 如果a[K]比队尾大,那么显然直接让队尾到队头降序扔到a[K]的后面就行了。

  • 如果a[K]比队尾小,那么如果直接再按刚才的降序直接扔,显然LIS会变长,所以让队尾到队列中第一个比a[K]大的数都降序扔到a[K]的前面,剩下的再扔到a[K]的后面就行了。

T1
#include <iostream>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dendl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 200005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
int n, K, L, R;
char is[N];
int a[N], ans[N], q[N];
void work(){
	cin >> n >> K;
	for (re i = 1 ; i <= K ; ++ i)
		{cin >> a[i]; is[a[i]] = true;}
	L = 0, R = -1;
	for (re i = 1 ; i <= n ; ++ i)
		if (is[i] == false)
			q[++ R] = i;
	for (re i = 1 ; i <= K-1 ; ++ i){
		ans[++ ans[0]] = a[i];
		if (q[L] < a[i] and L <= R)
			ans[++ ans[0]] = q[L ++];
	}
	/*for (re i = 1 ; i <= n ; ++ i)
		cout << ans[i] << _;
	Endl;*/
	if (a[K] < n)
		while (q[R] > a[K] and L <= R)
			ans[++ ans[0]] = q[R --];
	ans[++ ans[0]] = a[K];
	/*for (re i = 1 ; i <= n ; ++ i)
		cout << ans[i] << _;
	Endl;*/
	while (L <= R)
		ans[++ ans[0]] = q[R --];
	for (re i = 1 ; i <= n ; ++ i)
		cout << ans[i] << _;
	Endl;
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

T2 

一个简单的一维dp

fi为转移到i,并且必选i的方案数,初始值f[1]=1

考虑如何转移。

考虑这样一个数列:

4 6 3 1 2 3 5 4

在搞到第二个3的时候,f值只能由上一个3到当前位置转移,用柿子表达出来就是

fi=j=lastaii1fj

last就是上一个的意思。

考虑为什么从1lastai1不转移。

1lastai的方案,对于再添上一个lastai而言和添上一个ai而言是等同的,因为lastai就和ai一样嘛只不过位置不同,但是这样一来就不满足唯一性了。

就比如序列3 6是合法的(转移到目前而言),归属于f2;而4 6 3是不合法的,因为转移到目前发现有超过一个的3

考虑为什么要转移flastai

对于序列3,本身不合法,但是当转移的时候要填上他,因为序列3 3是合法的。

但是转移完之后flastai要删去,因为向4 6 3这样的序列是不合法的。

最后求一个前缀和就行了,可以用树状数组维护。

T2
#include <iostream>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dendl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 200005
#define P 998244353
#define mod %
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
/*
	一维dp是吧
	我想想,树状数组
	树状数组
	好久不用了
	一维dp
	哎哎,我啥时候能学会dp
	flag:学完线性筛就去打背包dp
*/
long long n;
long long a[N], f[N], plc[N], C[N<<2];// 树状数组要开多大来着
#define lowbit(x) ((x) & (-(x)))
inline void Update(long long k, long long ed, long long w){
	while (k <= ed){
		C[k] = (C[k] + w + P) mod P;
		k += lowbit(k);
	}
}
inline long long Query(long long k){
	long long res(0);
	while (k != 0){
		res += C[k];
		if (res >= P)
			res -= P;
		k -= lowbit(k);
	}
	return res;
}
inline void work(){
	cin >> n;
	for (re i = 1 ; i <= n ; ++ i)
		cin >> a[i];
	/*for (re i = 2 ; i <= n ; ++ i){
		f[i] = 1;
		if (plc[i] != 0)
			Update(1, n, -f[plc[i]]);
		f[i] += Query(i);
		if (f[i] >= P)
			f[i] -= P;
		Update(
	}*/
	f[1] = 1; plc[a[1]] = 1; Update(1, n, 1);
	for (re i = 2 ; i <= n ; ++ i){
		f[i] = Query(i-1);
		if (plc[a[i]] != 0){
			f[i] = (f[i] - Query(plc[a[i]]-1) + P) mod P;
			Update(plc[a[i]], n, -f[plc[a[i]]]);
		}
		else 
			f[i] ++;
		if (f[i] >= P)
			f[i] -= P;
		plc[a[i]] = i;
		Update(i, n, f[i]);
	}
	cout << Query(n) << '\n';
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

T3 GCD

这个题我上来一眼丁真认定为场切题O(nlogn),发颠分解了个质因数发现P用没有,之后又尝试二分gcd发现假了,然后就搞了随只因化

然后喜提0pts

正解很简单,分两种情况

mxai中最大值

K>=i=1nmxai:

显然要先让所有的ai达到mx,此时gcd就是mx,然后贪心地使gcd不断+1并判断当前K值能否达到。

可以直接写成一个柿子dsu

fst为所有ai达到mx所需要的和,那么最后答案即为mx+Kfstn

这玩意不难理解吧,mx已经是基础了,所有ai的值都是mx了这时候所有ai同时+1他们的gcd也就会同时+1,也就是那个柿子

K<i=1nmxai:

这就有意思了

考虑枚举他们的公共因子factor。那么如果让每个ai拥有该因子,根据显然的贪心策略,每个ai肯定是变成比他大的第一个的factor的倍数。

那么达到该状态所需要消耗的+1次数就是:

i=1naifactor×factorai

这样做是n2的。

考虑如何优化。

我们可以把ai升序排序一下,然后枚举aifactor也就是factor的倍数,二分找到他应该在a数组中的位置,减去上一个倍数所在位置,然后直接累加。根据调和级数这样做是只带一个log的。

所以就拆掉了一个n变成了logn。总时间复杂度为O(nlogn)(n与值域同阶)。

然后可以拆一下原柿子,就直接把i=1nai拆出来,也就是求一下ai的总和,不用在刚才二分的过程中减来减去,最后直接减就完了。

然后这题就做完了,总体思路十分简单,代码实现也不难,确实可以算是场切题

但是我这个shab赛时怎么就没想到。。。

T3
#include <iostream>
#include <algorithm>
#include <cmath>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dendl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 300005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
/*
	枚举倍数
	调和级数
	蛮巨的
	分两种情况,
	算了在草稿纸上胡了一遍就不在这里写了
*/
long long n, K, mx, first_step, sum, final_ans;
long long a[N];
inline void work(){
	cin >> n >> K;
	for (re i = 1 ; i <= n ; ++ i)
		{cin >> a[i]; mx = MAX(mx, a[i]); sum += a[i];}
	
	for (re i = 1 ; i <= n ; ++ i)
		first_step += (mx-a[i]);
	if (first_step <= K)
		{goto Another_Me;}
	
	sort(a+1, a+n+1);
	for (long long factor = mx, plc, res, ed, lst ; factor >= 1 ; -- factor){
		res = -sum; ed = ceil((double)mx / factor); lst = 0;
		for (long long bs = 1 ; bs <= ed ; ++ bs){
			plc = upper_bound(a+1, a+n+1, bs*factor) - a - 1;
			res += bs * factor * (plc-lst);
			// cerr << factor << _ << lst << _ << plc << _ << bs << _ << res << '\n';
			lst = plc;
		}
		// cerr << '\n' << '\n';
		if (res <= K)
			{final_ans = factor; break;}
	}
	cout << final_ans << '\n';
	return ;
	Another_Me:{
		final_ans = mx + (K-first_step)/n;
		cout << final_ans << '\n';
	}
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

T4 

(其实我也不是特别明白)

(开始大量转发)

观察K的数据范围 不难想到状压。
dps为选到的数的集合为 s的最少移动次数。
先预处理一下每个状态选了几个数(有多少个1)。
转移的话就是看看这个数有没有在集合里:如果不在,就加上这个数所移动的步数(逆序对数)。
每种集合还要加上 集合向右平移的步数 或者 让还没有选的数集体向左平移,二者取 min
———迪神

T4
#include <iostream>
#include <cstring>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dendl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 300005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}
int n, K, S;
int a[N], f[(1<<16) + 5], cnt[(1<<16) + 5];
/*
	“浮华的二进制。”
*/
#define lowbit(x) ((x) & (-(x)))
inline void work(){
	cin >> n >> K; S = (1 << K) - 1;
	for (re i = 1 ; i <= n ; ++ i)
		cin >> a[i];
	for (re i = 0, x ; i <= S ; ++ i){
		x = i;
		while (x != 0)
			cnt[i] ++, x ^= lowbit(x);
	}
	/*Dendl;
	for (re i = 0 ; i <= S ; ++ i)
		cerr << cnt[i] << _;
	Dendl;*/
	memset(f, 0x5f, sizeof(f));
	f[0] = 0;
	for (re i = 1, x ; i <= n ; ++ i){
		x = 1 << (a[i]-1);
		for (re s = S ; s >= 0 ; -- s){
			f[s|x] = MIN(f[s|x], f[s] + cnt[s&(-x)]);
			f[s] += MIN(cnt[s], K-cnt[s]);
		}
		// cerr << f[S] << '\n';
	}
	cout << f[S] << endl;
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

Postscript

(图我给扔到酰铧里了)

posted @   char_phi  阅读(46)  评论(6编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示