26 October in 614

Practice

tower

\(N\,(2\le N\le 600000)\) 块砖,要搭一个 \(N\) 层的塔,要求:如果砖 \(A\) 在砖 \(B\) 上面,那么 \(A\) 不能比 \(B\) 的长度 \(+D\) 要长。问有几种方法,输出答案 \(\bmod 1\,000\,000\,009\) 的值。

此题无法暴力。

观察,发现对于任意一块砖,其可插入在长度为 \([\text{len},\text{len}+D]\) 的任一块砖之上;对于任意一块砖,其插入位置与其他砖的摆放无关。由此可应用分步乘法计数原理(乘法原理)。

复习 lower_boundupper_bound

lower_boundupper_bound 本质是二分查找,时间复杂度为对数级别:

  • lower_bound(begin, end, val):从数组的 begin 位置到 end-1 位置二分查找第一个大于或等于 val 的数字,找到返回该数字的地址,不存在则返回 end。通过返回的地址减去起始地址 begin,得到找到数字在数组中的下标。
  • upper_bound(begin, end, val):从数组的 begin 位置到 end-1 位置二分查找第一个(严格)大于 val 的数字,找到返回该数字的地址,不存在则返回 end。通过返回的地址减去起始地址 begin,得到找到数字在数组中的下标。
  • lower_bound(begin, end, val, greater<type>()):从数组的 begin 位置到 end-1 位置二分查找第一个小于或等于 val 的数字。
  • upper_bound(begin, end, val, greater<type>()):从数组的 begin 位置到 end-1 位置二分查找第一个(严格)小于 val 的数字。
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long

const ll mod=1000000009;

int n, d, g[600002]; ll ans=1, sum;

int main() {
	scanf("%d%d", &n, &d);
	for (int i=1; i<=n; i++) scanf("%d", g+i);
	sort(g+1, g+n+1);
	for (int i=1; i<n; i++) {
		sum=upper_bound(g+i+1, g+n+1, g[i]+d)-lower_bound(g+i+1, g+n+1, g[i])+1;
		ans=ans*sum%mod;
	}
	printf("%lld\n", ans);
	return 0;
}

[NOI导刊2011提高01] 单词分类

分类 \(N\) 个单词,单词均由大写字母组成。两个单词可以分为一类当且仅当组成这两个单词的各个字母的数量均相等。例如“AABAC”,它和“CBAAA”就可以归为一类,而和“AAABB”就不是一类。求所有分类的总数。

复杂字符串处理如果没有意外的话,应该要用 string 来替代 cstring

同理要包含 iostream 库。此时要应用 cin/cout 优化:

ios::sync_with_stdio(false);
cin.tie(0);

对于本题,先对每个字符串做排序,这样分为一类的两个字符串就自动等价。然后对所有字符串统一去重,去重后的个数即为分类数。

代码:

#include <string>
#include <algorithm>
#include <iostream>
using namespace std;

int n;
string s[10002];

int main() {
	cin>>n;
	for (int i=0; i<n; i++) {
		cin>>s[i]; sort(s[i].begin(), s[i].end());
	}
	sort(s, s+n);
	n=unique(s, s+n)-s;
	cout<<n<<endl;
	return 0;
}
posted @ 2018-10-26 09:23  greyqz  阅读(148)  评论(0编辑  收藏  举报