ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOIp-75

2021-10-12 22:00:24  阅读:143  来源: 互联网

标签:ch NOIp int t4 t3 75 sum getchar


水博客太快乐了

RT

烤场

顺序开题。
啥也不会。。。

写了 \(t1, t3, t4\) 的暴力,t2懒就没有写,发现了 \(t4\) 的一些结论,但是不全,最终也没有什么用处。。。

最后十分钟想出 \(t3\) 正解,没来的及写,考完写了就过了。。。

总之貌似烤的挺炸的。。全是暴力。。。

分数

预估 :\(t1 \ 30pts \ + \ t2 \ 0pts \ + \ t3 \ 50pts \ + \ t4 \ 50pts \ = \ 130pts\)
实际 :\(t1 \ 30pts \ + \ t2 \ 0pts \ + \ t3 \ 50pts \ + \ t4 \ 30pts \ = \ 110pts\)

题解

A. 如何优雅的送分

本蒟蒻对这种数论的理解也并不是很深刻,建议移步巨佬 y_cx 的博客

B. 阴阳

巨佬cyh :暴力踩标算,直接暴力踩过去。

记录巨佬的方法:用主席树记录点的颜色暴力 bfs 找答案。

C. 你猜是不是找规律

提供一个不同于题解的方法。。。
考试最后 \(10min\) 想到,没来得及写。

最开始想的 \(dp\) 状态是 :\(f_{i,j}\) 表示长度为 \(i\) ,交换 \(j\) 次后变为顺序的排列数量,转移类似错排数的递推式,但是这样转移显然是 \(O(nk)\) 的,直接 \(T\) 飞。考虑优化。

发现 \(k\) 小的可怜,而 \(n\) 大的可怕,因此在最终的到的序列中,大部分的数都是有序的,只有少部分数无序,因此考虑只对无序的数进行 \(dp\) ,设计状态为 :\(f_{i,j}\) 表示长度为 \(i\) ,交换 \(j\) 次会变有序的错排列数量 (指 \(\forall i, a_i \ne i\))。
转移也类似与错排数,此处只给出转移,不加以赘述。。。

\[f_{i,j}=(i-1)(f_{i-1,j-1}+f_{i-2,j-1}) \]

不难发现只要求出 \(i \le 2k ,j \le k\) 的所有情况即可(因为总共就这么几种情况。。。),复杂度 \(O(k^2)\) 。
最终的答案就是将求出的所有无序序列插入一个有序序列即可。

\[ans=\sum_{i=0}^{2k} \binom{n}{i} \sum_{j=0}^k f_{i,j} \]

code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e3+10, mod=1e9+7;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, k, ans, f[4][N], sum[N<<1];
inline int qp(int n, int m){
	int ans=1;
	while(m){
		if(m&1) (ans*=n)%=mod;
		m>>=1; (n*=n)%=mod;
	}
	return ans;
}
inline int C(int n, int m){
	int mul=1, inv=1;
	for(int i=n-m+1; i<=n; ++i) (mul*=i)%=mod;
	for(int i=1; i<=m; ++i) (inv*=i)%=mod;
	return mul*qp(inv, mod-2)%mod;
}
signed main(void){
	n=read(); k=read(); f[0][0]=1; sum[0]=1;
	for(int i=2; i<=k<<1; ++i){
		memset(f[i%3], 0, sizeof(int)*(k+1));
		for(int j=1; j<=k; ++j)
			f[i%3][j]=(i-1)*(f[(i-1)%3][j-1]+f[(i-2)%3][j-1])%mod;
		for(int j=0; j<=k; ++j) (sum[i]+=f[i%3][j])%=mod;
	}
	for(int i=0; i<=k<<1; ++i)
		(ans+=sum[i]*C(n, i)%mod)%=mod;
	printf("%lld\n", ans);
}

D. 小说

首先 \(a\) 很好求,依次删去每个数跑一次背包,得到能容量种类数最多的即是 \(a\) 。这一定是正确的,设当前最大容量为 \(maxn\) ,种类数量为 \(num\) ,则一定可以使 \(b=maxn+1\) ,种类数会便为 \(2num+1\)。
可以用退背包的技巧,也可以用 \(bitset\) 优化。

接下来求 \(b\) ,考虑 \(b\) 需要满足什么条件,对于任意两个合法容量 \(x, y, x \le y\) ,都有 \(x+b \ne y\),即 \(b \ne x-y\) 。
\(x, y\) 是由 \(v\) 构成的,那么 \(b\) 就不可以由 \(v\) 之间相互加减得到,也就是将所有 \(v_i, -v_i\) 放在一起跑一个背包,最小无法被构成的数即为最小的 \(b\) 。

code
#include<bits/stdc++.h>
using namespace std;
const int N=110, V=7e5+10;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, a[N], maxn, a1, b1, sum;
bitset<V> ul;
signed main(void){
	n=read();
	for(int i=1; i<=n; ++i) sum+=a[i]=read();
	sort(a+1, a+1+n);
	for(int i=1; i<=n; ++i){
		ul.reset(); ul[0]=1;
		for(int j=1; j<=n; ++j){
			if(i==j) continue;
			ul|=(ul<<a[j]);
		}
		int num=ul.count();
		if(num>maxn) maxn=num, a1=i;
	}
	ul.reset(); ul[0]=1;
	for(int i=1; i<=n; ++i){
		if(i==a1) continue;
		ul|=(ul<<a[i]);
	}
	for(int i=1; i<=n; ++i){
		if(i==a1) continue;
		ul|=(ul>>a[i]);
	}
	for(int i=1; i<=sum-a[a1]+1; ++i)
		if(!ul[i]) { b1=i; break; }
	printf("%d %d\n", a[a1], b1);
	return 0;
}

summary

成为了史上最水博客。。。

标签:ch,NOIp,int,t4,t3,75,sum,getchar
来源: https://www.cnblogs.com/CTcode/p/NOIP_75.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有