CSP-S模拟 12

T1 #1699. 114514 \(100pts\)
签到计数。
- 正解:显然想要在当前位置填数需要之前已经出现过足够相同的数,或者填这个数本身,维护值域连续段即可。并查集直接 \(O(n)\) 做完了。当然也放过去了 \(O(n \log n)\) ,感觉放水了。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6 + 5;
const int M = 4e6 + 5;
const int mod = 1e9 + 7;
inline int read()
{
int x = 0, c = getchar(), f = 0;
for (; c > '9' || c < '0'; f = c == '-', c = getchar())
;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return f ? -x : x;
}
inline void write(int x)
{
if (x < 0)
x = -x, putchar('-');
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int n, a[N], sum = 1;
int t[N << 1];
int fa[N << 1];
int find(int x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
return;
fa[y] = x;
}
signed main()
{
freopen("trans.in", "r", stdin);
freopen("trans.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 1; i <= M - 5; i++)
fa[i] = i;
for (int i = 1; i <= n; i++)
{
t[a[i]] = 1;
if (t[a[i] - 1] != 0)
{
int ans = find(a[i] - 1);
// cerr << i << ans;
sum = sum % mod * (a[i] - ans + 1) % mod;
}
if (t[a[i] - 1])
merge(a[i] - 1, a[i]);
if (t[a[i] + 1])
merge(a[i], a[i] + 1);
}
write(sum % mod);
}
T2 #1700. 沉默乐团 \(40pts\)
又是计数。
很妙的 DP 题。你问为啥是 40 分?因为数据造错了一组。
- 部分分: 搜 + 特殊性质。
点击查看代码
#include <bits/stdc++.h>
#define int long long
const int N = 55;
const int V = 2005;
const int mod = 1e9 + 7;
using namespace std;
inline int read()
{
int x = 0, c = getchar(), f = 0;
for (; c > '9' || c < '0'; f = c == '-', c = getchar())
;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return f ? -x : x;
}
inline void write(int x)
{
if (x < 0)
x = -x, putchar('-');
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int n, maxn;
int s[N], ans;
struct node
{
int l, r;
} a[N];
int qpow(int x, int y)
{
int mi = x, ans = 1;
while (y > 0)
{
if (y & 1)
ans = ans * mi % mod;
mi = mi * mi % mod;
y >>= 1;
}
return ans % mod;
}
void dfs(int x)
{
// for (int i = 1; i <= n; i++)
// cout << s[i];
// cout << '\n';
if (x == n + 1)
{
int sum1 = 0, sum2 = 0;
bool f = 1;
for (int i = 1; i < n; i++)
{
sum2 = 0;
sum1 += s[i];
for (int j = n; j >= 2; j--)
{
sum2 += s[j];
if (sum1 == sum2)
{
f = 0;
break;
}
}
if (f == 0)
break;
}
// if (f == 1)
// for (int i = 1; i <= n; i++)
// cout << s[i];
ans += f;
return;
}
for (int i = a[x].l; i <= a[x].r; i++)
{
s[x] = i;
dfs(x + 1);
}
}
signed main()
{
freopen("orchestra.in", "r", stdin);
freopen("orchestra.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++)
a[i].l = read(), a[i].r = read(), maxn = max(a[i].r, maxn);
if (n <= 8 && maxn <= 8)
{
dfs(0);
cout << ans;
return 0;
}
else
{
ans = (a[1].r - a[1].l + 1) * qpow(a[1].r - a[1].l, n - 1) % mod;
cout << ans;
return 0;
}
}
- 正解:限制完全可以转化为不存在一对前缀的和与后缀的和相等。注意到相交部分无用,所以只考虑不相交的两部分。观察数据范围很小,于是我们考虑大力 DP 。
使用双指针,当前缀和大于后缀和就左移右指针,反之则反之。设 \(f_{i,j,k}\) 表示当指针分别在 \(i\) 、 \(j\) 时前后缀相差 \(k\) 时的方案数,为处理负数加上 \(V\) 的偏移量。转移时按照上述思想进行转移,枚举因指针移动产生的新元素 \(x\) , \(x\in[l_{i} , r_{i}]\) 。答案即为 \(f_{1,n,V}\) 。发现直接转移高达 \(O(n^2V^2)\) ,只需要上一个前缀和即可达到 \(O(n^2V)\) ,可以通过。
点击查看代码
#include <bits/stdc++.h>
#define int long long
const int mod = 1e9 + 7;
const int N = 55;
const int V = 2000;
using namespace std;
int n;
int L[N], R[N];
int f[N][N][4005];
int sum[N][N][4005];
signed main()
{
freopen("orchestra.in", "r", stdin);
freopen("orchestra.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> L[i] >> R[i];
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= V * 2; j++)
if (j != V || n == 1)
f[i][i][j] = R[i] - L[i] + 1 - (abs(j - V) <= R[i] && abs(j - V) >= L[i]);
for (int j = 1; j <= V * 2; j++)
sum[i][i][j] = (sum[i][i][j - 1] + f[i][i][j]);
}
for (int mr = 2; mr <= n; mr++)
for (int i = 1, j = mr; j <= n; i++, j++)
{
for (int k = 0; k <= V * 2; k++)
if (k != V || (i == 1 && j == n))
f[i][j][k] = (sum[i + 1][j][k + R[i]] - sum[i + 1][j][k + L[i] - 1] + mod) % mod;
for (int k = V + 1; k <= V * 2; k++)
f[i][j][k] = (sum[i][j - 1][k - L[j]] - sum[i][j - 1][k - R[j] - 1] + mod) % mod;
sum[i][j][0] = f[i][j][0];
for (int k = 1; k <= V * 2; k++)
sum[i][j][k] = (sum[i][j][k - 1] + f[i][j][k]) % mod;
}
cout << f[1][n][V];
}
T3 #1701. 深黯 \(40pts\)
还是计数。
- 部分分:预处理优化暴力。
点击查看代码
#include <bits/stdc++.h>
const int N = 5e5 + 5;
#define int long long
using namespace std;
inline int read()
{
int x = 0, c = getchar(), f = 0;
for (; c > '9' || c < '0'; f = c == '-', c = getchar())
;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return f ? -x : x;
}
inline void write(int x)
{
if (x < 0)
x = -x, putchar('-');
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int n, k, mod, ans;
int a[N];
int num[N], jc[N];
signed main()
{
freopen("army.in", "r", stdin);
freopen("army.out", "w", stdout);
n = read();
k = read();
mod = read();
jc[0] = 1;
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 1; i <= n; i++)
{
jc[i] = (jc[i - 1] * i) % mod;
if (i == 1)
num[i] = 0;
else
num[i] = (num[i - 1] * i % mod + jc[i - 1] * ((i * (i - 1) / 2)) % mod) % mod;
//cout << num[i] << '\n';
}
if (n <= 18)
{
ans = (ans + k / jc[n] * num[n]) % mod;
k %= jc[n];
}
while (1)
{
if (k == 0)
break;
do
{
if (k == 0)
break;
k--;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i - 1; j++)
ans = (ans + (a[i] < a[j])) % mod;
}
} while (next_permutation(a + 1, a + n + 1));
sort(a + 1, a + n + 1);
}
cout << ans;
}
- 正解:考虑拆位找贡献。可以显然出规律。直接暴力拆位做。不妨将一整个周期作为大块,较小位数周期作为中块,最后可以暴力部分作为三块。
由于最多只有 \(1e18\) 次,所以 \(n>19\) 时无大块,剩下的复杂度就很对了,大约是 \(\log\) 级别,可以轻松跑过。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) (x & -x)
const int N = 5e5 + 5;
const int V = 2e18;
using namespace std;
int n, k, mod, ans;
int a[N], t[N];
int jc[N];
void add(int x, int v)
{
for (int i = x; i <= n; i += lowbit(i))
t[i] += v;
}
int query(int x)
{
int sum = 0;
for (int i = x; i >= 1; i -= lowbit(i))
sum += t[i];
return sum;
}
signed main()
{
freopen("army.in", "r", stdin);
freopen("army.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> k >> mod;
for (int i = 1; i <= n; i++)
cin >> a[i];
jc[0] = 1;
for (int i = 1; i <= n; i++)
{
if (i > 19)
jc[i] = V;
else
jc[i] = jc[i - 1] * i;
}
int las = 1, num, len;
for (int i = n; i >= 1; i--)
{
num = query(a[i]);
if (las > k)
{
ans = (ans + k % mod * num) % mod;
goto skk;
}
ans = (ans + (las - 1) % mod * num) % mod;
if (las > 1)
num++;
for (; num <= n - i; num++)
{
if (las + jc[n - i] - 1 > k)
{
ans = (ans + (k - las + 1) % mod * num) % mod;
las = V;
goto skk;
}
ans = (ans + jc[n - i] % mod * num) % mod;
las += jc[n - i];
}
ans = (ans + ((k - las + 1) / jc[n - i + 1] % mod * (jc[n - i] % mod) % mod * ((n - i + 1) * (n - i) / 2 % mod))) % mod;
len = (k - las + 1) % jc[n - i + 1];
for (int j = 0;; j++)
{
if (len < jc[n - i])
{
ans = (ans + len % mod * j) % mod;
break;
}
ans = (ans + jc[n - i] % mod * j) % mod;
len -= jc[n - i];
}
skk:;
add(a[i], 1);
}
cout << ans;
}
T4 #1702. 终末螺旋 \(0pts\)
不可做计数。
喜欢坐牢的小朋友们自己改去吧。
总结
- 怎么还是困?
- T2 拼尽全力无法想出。
- T3 拼尽全力只会了预处理。
- T4 场上无人有分,部分分等于没有。
- 题目据说是私货,咱不玩脑叶咱也不知道 o(〃^▽^〃)o

浙公网安备 33010602011771号