首页 > 其他分享 >CSP模拟-4

CSP模拟-4

时间:2023-07-28 20:56:48浏览次数:20  
标签:int dp long 5211314 序列 include CSP 模拟

日,怎么第一天考试直接4道思维题,被真实力..........

T1 [ARC125C] LIS to Original Sequence

这道题还是比较简单的 能想到

由于题目里面让求字典序最小,因此我们可以隐约的想到做法:贪心。

我们现在将一个 \(1\) 到 \(n\) 的数列分成输入的数和除输入以外的数。然后我们再将 \(a_k(k>1)\) 的最长上升子序列分成两个部分:\(a_1,a_2,a_3,\dots,a_{k-1}\) 和 \(a_k\)。

Part 1(第一个序列)

对于一个数 \(x\),如果它小于 \(a_i\),则 \(x\) 一定不可以放在 \(a_i\) 前面,因为它会造成最长上升子序列的长度增大,因此我们只能把它放在 \(a_i\) 后面序列。

如果我们放多个比 \(a_i\) 小的数在 \(a_i\) 后面,让它们顺序排,会使最长上升子序列变长;如果我们让它们逆序排,不符合字典序最小的要求,所以只能放一个。

接下来对 \(a_1\) 到 \(a_{k-1}\) 进行操作即可。

Part 2(第二个序列)

先来说说我们为什么分成两个序列。我们如果对于 \(a_k\) 依照上面处理的方式处理的话。我们后面剩下的数该怎么处理呢?\(a_k\) 后面的数不可能再比 \(a_k\) 大,那样的话会使最长上升子序列的长度变长,所以 \(a_k\) 后面的数一定要比 \(a_k\) 的值小。这样,我们处理后一个序列的方式就出现了。将我们插剩下的数与 \(a_k\) 进行排序,逆序输出就行。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

using namespace std;

int n, k, a[5211314];
int num[5211314], sum, pos = 1;

int main() {
	cin >> n >> k;
	for (int i = 1; i <= k; ++ i) {
		cin >> a[i];
	}
	for (int i = 1, now = 1; i <= n; ++ i) {
		if (i != a[now]) {
			num[++ sum] = i;
			//将不是输入的数从小到大存起来 
		}
		else now ++;
	}
	num[sum + 1] = 1e9;//防止在下面的if里一直输出0 
	for (int i = 1; i <= k - 1; ++ i) {
		cout << a[i] << " ";
		if (num[pos] < a[i]) {
			//找不在输入序列里第一个比a[i]小的数,插入到前面
			//可以得到字典序最小 
			cout << num[pos] << " ";
			pos ++;
		}
	}
	if (pos <= sum) {
		//特殊处理a[k] 
		for (int i = sum; i >= pos; -- i) {
			if (a[k] > num[i]) {
				cout << a[k] << " ";
				a[k] = -1;
			}
			cout << num[i] << " ";
		}
		if (a[k] != -1) {
			cout << a[k] << endl;
		}
		//将剩下的数从大到小输出 
	}
	else {
		cout << a[k];
		//只剩下a[k]的情况特殊看 
	}
	return 0;
}

T2 [ARC125D] Unique Subsequence

这边建议这篇[题解]([ARC125D] Unique Subsequence - int_R の blog - 洛谷博客 (luogu.com.cn))

但这篇巨佬的题解里有一些我认为的错误,在代码里面标出来了

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define mod 998244353

using namespace std;

long long n, a[521314], same[5211314], las[5211314];
long long tree[5211314], f[5211314];

class BinaryIndexedTree {
	
	private:
		int lowbit(int x) {
			return (x & (-x));
		}
	public:
		void Add(int x, int num) {
			while (x <= n) {
				tree[x] = (tree[x] + num) % mod;
				x += lowbit(x);
			}
		}
		long long Query(int x) {
			long long ans = 0;
			while (x != 0) {
				ans = (ans + tree[x]) % mod;
				x -= lowbit(x);
			}
			return ans;
		}
}Tree;



int main() {
	cin >> n;
	for (int i = 1; i <= n; ++ i) {
		cin >> a[i];
		same[i] = las[a[i]];
		las[a[i]] = i;
	} 
	for (int i = 1; i <= n; ++ i) {
		if (same[i] != 0) {
			f[i] = (Tree.Query(i - 1) - Tree.Query(same[i] - 1) + mod) % mod;
			//这里不能将same[i]前面的方案数加上.
			//i只能在same[i]到i-1转移,因为从1,pre-1中转移到i的也可以转移到same[i]所以代表取出方案并不唯一 
			//注意 这里找same[i]-1 是因为same[i]可以组成的方案后面加上a[i]也不会重复 要加上 
			//如a[5]=4,same[5]=4,若f[same[i]]有 1 2 3 4的方案.则f[i]有方案 1 2 3 4 4,不重复 
			//而不是因为same[i]取得方案与a[i]可以取得方案有重复才取到same[i]-1 
			Tree.Add(same[i], -f[same[i]]);
			//此处题解可能理解错了 
			//将f[same[i]]的值赋成0。因为a[i]与same[i]重复了,不能将same[i]的方案数加上 
			/*若存在j>i,则j不能从same[i]转移,因为same[i]与a[i]重复了,所以不能取same[i]的方案。
			  而是要取same[i]的方案后面加上a[i]的方案*/ 
		}
		else {
			f[i] = (Tree.Query(i - 1) + 1);
			//这里的加1是将它本身为一个单独子串的方案数加上 
		}
		Tree.Add(i, f[i]);
	}
	cout << (Tree.Query(n) + mod) % mod << endl;
	return 0;
} 

T3 [ARC126C] Maximize GCD

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

long long n, k, a[5211314], maxn; 
//maxn表示输入的最大的数 
long long barrel[5211314];
long long sum1[5211314], sum2[5211314], maxsum;
//sum1[i] 表示值1到i有几个数 sum2[i]表示值为1到i的数和 
//maxsum表示把每个数加到maxn的花费 

int main() {
	cin >> n >> k;
	for (long long i = 1; i <= n; ++ i) {
		cin >> a[i];
		maxn = max(maxn, a[i]);
		barrel[a[i]] ++;
	} 
	for (long long i = 1; i <= 2 * maxn; ++ i) {
		sum1[i] = sum1[i - 1] + barrel[i];
		sum2[i] = sum2[i - 1] + barrel[i] * i;
	}
	for (long long i = 1; i <= n; ++ i) {
		maxsum += (maxn - a[i]);
		//所有的数加到maxn的花费
	}
	if (maxsum <= k) {
		//看k能不能满足花费
		printf("%lld\n", maxn + (k - maxn) / n);
		return 0; 
	} 
	for (long long i = maxn; i >= 1; -- i) {
		//枚举因数
		maxsum = 0;
		for (long long j = 1; (j - 1) * i <= maxn; ++ j) {
			long long cnt;
			cnt = sum1[j * i - 1] - sum1[(j - 1) * i];
			//有多少个数在 ((j-1)*k, j*k] 的区间内
			maxsum += cnt * j * i - (sum2[j * i - 1] - sum2[(j - 1) * i]);
			//maxsum加的表示将上述区间内的数均加到j*k的花费
		}
		if (maxsum <= k) {
			printf("%lld\n", (long long)i);
			return 0;
		}
	}
	return 0;
} 

T4 [ARC126D] Pure Straight

我们这道题要用逆天的 dp 做(考场没想到),考试后也是请教了巨佬才明白 。

题意有很多题解都讲的很好,就不在这里过多赘述了。

看到 \(k<=16\) 的时候,我们可以想到用 \(2^k\) 的时间复杂度来做,因此就会自然的想到状态压缩 dp。

我们设 \(dp_{i,S}\) 表示转移到第 \(i\) 个元素时,我们选取元素所构成的集合为 \(S\),这时我们移动的次数为 \(dp_{i,S}\)。

我们会有两种情况转移过来:选或者不选。

  • 选现在的 \(a_i\),那么此时加入集合的花费即为比 \(a_i\) 大的数的个数。但是可能 \(a_i\) 转移到的状态有其他状态转移过来的更优解,所以要取 min。

  • 若不选,我们要么将现在处理好的集合 \(S\) 移到现在选的元素 \(i\) 的后面,要么将未来可能会移过来的数的花费先加上,也就是先加上 \(S\) 的补集的数的个数。这个的大概意思就是说将两边已经排好序的集合不断的向中间靠拢,这样才能保证花费最小。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

using namespace std;

int n, k, a[1000000], dp[1000000];

int main() {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; ++ i) {
		scanf("%d", &a[i]);
	}
	memset(dp, 0x3f, sizeof(dp));
	dp[0] = 0;
	for (int i = 1; i <= n; ++ i) {
		int mask = (1 << (a[i] - 1)) - 1, bit = (1 << (a[i] - 1));
		for (int j = (1 << k) - 1; j >= 0; -- j) {
			if ((j & bit) == 0) {
				dp[j | bit] = min(dp[j | bit], dp[j] + __builtin_popcount(j & (~mask)));
			}
			//若能选,就比较一下 
			dp[j] = dp[j] + min(__builtin_popcount(j), k - __builtin_popcount(j));
			//将不选的情况更新 至于为什么不是else 因为我们选了比较的情况下,我们也可以重新选择不选 
		}
	}
	cout << dp[(1 << k) - 1] << endl;
	return 0;
} 

总结

1.考试的时候一直跳题,所以一直没有想一道题,其实CSP-4模拟里除了T4 都可做。
2.第一次考试不太有

标签:int,dp,long,5211314,序列,include,CSP,模拟
From: https://www.cnblogs.com/jueqingfeng/p/17588874.html

相关文章

  • CSP模拟-7
    集合专练?????逆天!!!!!!!T1卷逆天!!!!!!!!!!!!!!!!!!!!又没看懂题。独立集指集合里的每个点不相连呜呜呜呜呜,我还以为是剩下的点互不相连,直接寄掉。式子好推,就不推了,咕。#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<cstring>#include<vector>#......
  • CSP模拟-8
    今天T1终于看懂辣。。。。但今天名次最低QAQ。T1没算空间复杂度,直接炸QAQT1Coprime2今天T1确实简单,将输入的数的质数公因数用埃氏筛筛出来,用一个数组存下来。每次将质因数的倍数用\(flag\)存下\(true\),表示这个数存在因数与输入的数重复的情况,让后就没有辣。正确性的话:......
  • CSP模拟8
    闲话今天老吕从国赛,带来一个消息:“省选可能取消,完全看NOIP成绩”。不过对我没什么影响,反而还开心一些。A.Coprime题目大意给定一个长度为\(n\)的数列\(a\),要求出\(1\simm\)中与\(a\)中的所有元素互质的数。数据范围:\(1\\leq\n,m\\leq\10^5,1\\leq\a_i\\l......
  • 「赛后总结」暑假集训:20230727 CSP 模拟赛
    「赛后总结」20230727CSP模拟赛点击查看目录目录「赛后总结」20230727CSP模拟赛总结题解T1卷T2简单题T3粉丝T4字符串已经入园两年了吗。好快哦。2023年7月28日20:04:早上就写完了但忘了发了。以下内容均写于「2023年7月27日」。前两天题还没改完呢,有......
  • wpf在设计器模式利用模拟数据展现控件
    使用VisualStudio开发WPF应用程序时,控件显示需要的数据如果来路比较“苦难”,比如来自数据库,JSON文件,复杂计算等,这时候,如果想看到控件带有数据的展示效果,需要启动调试,这很麻烦。我们可以在XAML中使用designtime语法给控件赋予模拟数据MSDN教程,也可以在后台使用csharp代码判断当......
  • c# WinForm 引用 Chrome 模拟操作
    Nuget CefSharp.WinForms publicForm1(){InitializeComponent();chromiumWebBrowser1.LoadingStateChanged+=ChromiumWebBrowser1_LoadingStateChanged;}privatevoidbutton1_Click(objectsender,EventArgs......
  • Window系统下模拟Linux环境的工具
    [b][color=red]强大的Cygwin[/color][/b]:[url]http://cygwin.com/install.html[/url]OracleunderCygwin-EduUnix[url]http://eduunix.ccut.edu.cn/index2/html/oracle/O'Reilly%20-%20Perl.For.Oracle.DBAs.eBook-LiB/oracleperl-CHP-2-SECT-4.......
  • Android shell模拟物理按键
    Androidshell模拟物理按键在Android开发中,有时候我们需要模拟物理按键的操作,例如模拟点击返回键、Home键等。Android提供了一个能够在命令行中模拟按键操作的工具——input。input命令简介input命令是Android系统中的一个工具,用于模拟按键事件。通过使用不同的参数,我们可以模拟......
  • 济南 CSP-J Day 4
    SolutionT1出现次数原题链接4102:出现次数简要思路利用类似前缀和的“后缀和”来记录下每个数后面有几个未重复出现的数,定义一个\(f\)数组来判断是不是第一次出现(实现去重)。完整代码#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constint......
  • CSP 模拟 6
    T1排序基本是原题CF1375E好像是简单题,考虑这个排列\(\pi\)的逆排列\(\pi^{-1}\)(如果排列是\(a_i\),则逆排列为\(b_{a_i}=i\)),因为逆序对的定义是序列编号和数值大小关系不一样,所以在逆排列中逆序对和原排列相同,对逆排列做冒泡排序,因为冒泡排序交换的是相邻值的位置,对其他值......