C 简单,D 有点意思,但没啥用,不写。
F 有点好玩,想了一个小时,看了一上午题解,弄明白了。
E - Snuke Line
\(n\) 个物品,第 \(i\) 个物品在 \([l_i,r_i]\) 间。
你初始在第 0 格,要走到第 \(m\) 格,求对于 \(1\sim m\) 每一种步长,走到的格子里的物品种类之和。
\(1\le n\le 10^5,1\le m\le 3\times 10^5\)
很有意思的题目。
首先有一种数论分块的做法,这里略过,可以参考洛谷题解。
这道题树状数组的解法很有参考意义,这里简要说一下:
首先,步长 \(\le r_i-l_i+1\) 的必定含第 \(i\) 种物品,原因显然,而步长 \(\gt r_i-l_i+1\) 只会经过 \([l_i,r_i]\) 至多一次。
离线下来,对于步长 \(x\),将所有 \(r_i-l_i+1\le x\) 的 \([l_i,r_i]\) 区间加,然后调和级数单点查询即可。这样显然不会漏算或多算。最后再加上区间长度大于 \(x\) 的即可。
时间复杂度 \(\mathcal O(n\ln n\log n)\)
Code
// Problem: E - Snuke Line
// Contest: AtCoder - AtCoder Regular Contest 068
// URL: https://atcoder.jp/contests/arc068/tasks/arc068_c
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define fir first
#define sec second
using pii = std::pair<int,int>;
const int maxn = 3e5 + 5;
int c[maxn],n,m;
pii a[maxn];
int lowbit(int x) {
return x & -x;
}
void add(int x,int y) {
for(;x <= m;x += lowbit(x))c[x] += y;
return ;
}
int query(int x) {
int ans = 0;
for(;x;x -= lowbit(x))ans += c[x];
return ans;
}
int main() {
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;++ i)
scanf("%d %d",&a[i].fir,&a[i].sec);
std::sort(a + 1 , a + 1 + n , [&](const pii& p,const pii& q) {
return p.sec - p.fir < q.sec - q.fir;
});
for(int i = 1,j = 1;i <= m;++ i) {
for(;j <= n&&a[j].sec - a[j].fir + 1 < i;++ j) {
add(a[j].fir , 1);
add(a[j].sec + 1 , -1);
}
int ans = 0;
for(int k = i;k <= m;k += i)
ans += query(k);
printf("%d\n",ans + n - j + 1);
}
}
F - Solitaire
给定一个双端队列,将 \(1\sim n\) 从头或尾插入队列。然后再依次从队头或队尾取出元素,形成一个序列,问 1 在序列的第 \(k\) 个位置的序列数量。
\(1\le k\le n\le 2000\)
很神奇的一道题。给出一种 \(\mathcal O(n^2)\) 的算法,似乎有基于 Dilworth 定理的 \(\mathcal O(n)\) 算法,不过我不会,就算了。
首先,根据 ARC F 这类计数题的套路,一开始不要纠结序列的具体形态,尝试思考序列的合法性,找一找序列的性质。
发现这个双端队列中的元素必然呈单谷状,设 \(Q_j=1\),则 \(Q_1\gt Q_2\gt\dots Q_j\lt Q_{j+1}\lt \dots\lt Q_n\)
要让 1 在第 \(k\) 位,就要把 \(Q_{1\sim j-1},Q_{n-(k-j)+1\sim n}\) 先取出来,然后把 \(Q_j\) 取出来。
那么剩下的 \(n-k\) 个数所能构成的序列数为 \(2^{n-k-1}\),我们要关注的是前面一段的数量。
考虑一个长为 \(k\) 的序列怎样才能符合我们的要求:根据双端队列的单谷性质,这个删除序列一定可以划分为两个互不相交的单调递减子序列,分别记为 \(A,B\),并且 \(A,B\) 其中之一的最小值要大于 \(\max\limits_{i\in [j+1,n-(k-j)]}Q_i\)
因为 \(A,B\) 有无序对整个序列无影响,不妨设 \(A\) 的最小值小于 \(B\) 的最小值,且 \(B\) 要满足上述性质(最小值大于那一串式子)
设 \(f(i,j)\) 表示序列中已有 \(i\) 个数,最小值为 \(j\) 时的方案数。
从 \(i-1\to i\) 不好推,我们考虑从 \(i\to i+1\) 这样转移状态。分情况讨论:
-
新添的数放入 \(A\) 中:设这个数为 \(p\),则 \(p\lt j\),\(f(i,j)\) 转移到 \(f(i+1,p)\)
-
新添的数放入 \(B\) 中:根据性质,放入的这个数必须是未选数的最大值,不超过 \(n-i\),所以若 \(n-i\lt j\) 就不满足 \(A\) 的最小值比 \(B\) 小的原则,故当 \(j\le n-i\) 时,\(f(i,j)\) 可以转移至 \(f(i+1,j)\)
最后统计下即可。
Code
// Problem: F - Solitaire
// Contest: AtCoder - AtCoder Regular Contest 068
// URL: https://atcoder.jp/contests/arc068/tasks/arc068_d
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using i64 = long long;
const i64 mod = 1e9 + 7;
const int maxn = 2005;
int n,k;
i64 f[maxn][maxn];
i64 power(i64 x,i64 y) {
i64 ans = 1;
for(;y;y >>= 1) {
if(y & 1)(ans *= x) %= mod;
(x *= x) %= mod;
}
return ans;
}
int main() {
scanf("%d %d",&n,&k);
if(n <= 2)return printf("1"),0;
if(k == 1)return printf("%lld\n",power(2 , n - k - 1)),0;
for(int i = 1;i <= n;++ i)f[1][i] = 1;
for(int i = 1;i < k - 1;++ i) {
f[i + 1][n - i + 1] = f[i][n - i + 1];
for(int j = n - i;j >= 2;-- j)
(f[i + 1][j] += f[i + 1][j + 1] + f[i][j]) %= mod;
}
i64 ans = 0;
for(int i = 2;i <= n - k + 2;++ i)
(ans += f[k - 1][i]) %= mod;
printf("%lld",ans * power(2 , std::max(0 , n - k - 1)) % mod);
return 0;
}
标签:AtCoder,le,Contest,int,068,i64,Regular,maxn,序列
From: https://www.cnblogs.com/Royaka/p/16858186.html