NOIP模拟测试A3
A. 谜之阶乘
题目是让我们把 \(n\) 分解成两个阶乘的商,本来想推个式子什么的,结果发现推不出来。
我们知道,阶乘的增长速率非常的快啊!那么这个 \(b - a\) 的值肯定不会太大,我们可以暴力枚举 \(b - a\) 的值。
假设我们选择 \(5\) 个连续的正整数的乘积为 \(n\),那么他们的值都在 \(\sqrt[5]{n}\) 附近。
那么假设 \(d = b - a\),显然有 \(\displaystyle a < \sqrt[d]{n}\),\(\displaystyle b < \sqrt[d]{n}\)。
我们就可以直接在 \(\sqrt[d]{n}\) 附近的范围枚举,枚举 \(20\) 个就差不多了。
复杂度大概是 \(\displaystyle O(T \ \text{log}_2^2 \ n)\)。
Code:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#define int long long
int T;
int n;
struct Node{
int first,second;
friend bool operator<(Node a,Node b) {
return a.first < b.first;
}
}res[100500];
int cnt;
signed main() {
std::cin >> T;
while(T--) {
std::cin >> n;
if(n == 1) {
std::cout << "-1\n";
continue;
}
cnt = 1;
res[cnt] = (Node){n,n - 1};
for(int i = 2;i <= 63; i++) {
int x = pow((double)n,(double)1 / (double)i);
while(1) {
int y = x - i + 1;
int k = 1;
for(int i = y;i <= x; i++)
k *= i;
if(k > n)
break;
if(k == n && y - 1 != 0) {
cnt ++;
res[cnt] = (Node){x,y - 1};
break;
}
x ++;
}
}
std::cout << cnt << "\n";
for(int i = cnt;i >= 1; i--)
std::cout << res[i].first << " " << res[i].second << "\n";
}
return 0;
}
B. 子集
记 \(m = \dfrac{n}{k}\)。
首先判掉无解的情况。
\(m = 1\) 时显然是无解的。
若 \(1\) 到 \(n\) 之和不是 \(k\) 的倍数,肯定无法分成相等的集合,也无解。
若 \(n\) 为偶数而 \(m\) 是奇数,也就符合了上面说的那种情况,无解。
再特判一下 \(k = 1\) 的情况,直接输出就可以了。
考虑 \(m\) 为偶数,那么我们可以直接将 \(i\) 与 \(n - i\) 配对,显然这样各集合元素之和是相等的。
若 \(n,m\) 均为奇数,我们把 \(m\) 看作 \(2x + 3\),对于 \(2x\) 我们像之前那样处理,只是特殊处理出前三列。
然后进行构造。
复杂度 \(O(n)\)。
Code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int T,n,k,m;
long long sum,cnt;
int l;
std::vector<int> f[500500];
signed main() {
cin >> T;
while(T--) {
for(int i = 0;i < 500500; i++)
f[i].clear();
cin >> n >> k;
m = n / k;
sum = n * (n + 1) / 2;
if(k == 1) {
printf("Yes\n");
for(int i = 1;i <= n; i++)
cout << i << " ";
cout << endl;
continue;
}
if(sum%k != 0 || m == 1 || n == k) {
// 有 n 组,不可能各组和相等
cout << "No" << endl;
continue;
}
if(n&1 == 0 && m&1 != 0) {
// 1 到 n 的和肯定不是 k 的倍数
cout << "No" << endl;
continue;
}
cout << "Yes\n";
if(m&1) {// m 为奇数
for(int i = 1;i <= (m - 3)/2; i++) {
l = (i - 1) * 2 * k;
for(int j = 1;j <= k; j++)
f[j].push_back(l + j);
for(int j = k;j >= 1; j--)
f[j].push_back(l + 2 * k - j + 1);
}
l = (m - 3) * k;
for(int i = 1;i <= k; i++)
f[i].push_back(l + i);
l = l + k;
for(int i = (k - 1)/2 + 1; i <= k; i++)
f[i].push_back(l + i - (k - 1)/2);
for(int i = 1;i <= (k - 1)/2; i++)
f[i].push_back(l + k - (k - 1)/2 + i);
for(int i = (k - 1)/2 + 1,m = n;i <= k; i++, m -= 2)
f[i].push_back(m);
for(int i = 1,m = n - 1;i <= (k - 1)/2; i ++ ,m -= 2)
f[i].push_back(m);
for(int i = 1;i <= k; i++) {
for(int j = 0;j < f[i].size(); j++)
cout << f[i][j] << " ";
cout << endl;
}
}
else {
for(int i = 1;i <= k; i++) {
for(int j = 1;j <= (m/2); j++) {
int l = 2 * (j - 1);
cout << (l * k + i) << " " << ((l | 1)*k + k - i + 1) << " ";
}
cout << endl;
}
}
}
return 0;
}
/*
Input:
4
4 4
4 1
12 3
9 3
*/
C. 混凝土粉末
把询问离线,在序列下标上执行扫描线,开一棵以询问编号为下标的树状数组。
扫到一个修改的左端点时,在树状数组上二分,扫到左端点加 \(h[i]\),扫到右端点时减去。
扫到询问时,在树状数组上二分。
Code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#define int long long
using namespace std;
const int N = 2005000;
int n,q;
int opt[N],res[N];
vector< pair<int,int> > g[N],h[N];
class BIT{
private:
int bit[N];
int lowbit(int x) {
return x & (-x);
}
public:
void Add(int x,int y,int val) {
for(int i = x;i <= y; i += lowbit(i))
bit[i] += val;
}
int Query(int x) {
int ans = 0;
for(int i = x;i; i -= lowbit(i))
ans += bit[i];
return ans;
}
}tree;
signed main() {
cin >> n >> q;
for(int i = 1,l,r,x,y;i <= q; i++) {
cin >> opt[i];
if(opt[i] == 1) {
cin >> l >> r >> x;
g[l].push_back(make_pair(i,x));
g[r + 1].push_back(make_pair(i,-x));
}
else {
cin >> x >> y;
h[x].push_back(make_pair(i,y));
}
}
for(int i = 1;i <= n; i++) {
for(int j = 0;j < g[i].size(); j++)
tree.Add(g[i][j].first,q,g[i][j].second);
for(int j = 0;j < h[i].size(); j++) {
int l = 1,r = h[i][j].first,mid;
while(l < r) {
mid = (l + r) >> 1;
if(tree.Query(mid) >= h[i][j].second)
res[h[i][j].first] = r = mid;
else
l = mid + 1;
}
}
}
for(int i = 1;i <= q; i++)
if(opt[i] == 2)
cout << res[i] << endl;
return 0;
}
D. 排水系统
Code:
#include <bits/stdc++.h>
#define int long long
const int N = 20090500;
const int MOD = 998244353;
int n,m,r,k;
struct Edge{
int next,to,val;
}e[N];
int h[N],cnt;
int in[N],out[N],sum;
int inv[N],inverse;
int f[N][2];
std::queue<int> que;
void Add(int u,int v,int w) {
cnt ++;
e[cnt].next = h[u];
h[u] = cnt;
e[cnt].to = v;
e[cnt].val = w;
return ;
}
int pow(int a,int b) {
int ans = 1;
while(b) {
if(b&1)
ans *= a;
ans %= MOD;
a *= a;
a %= MOD;
b >>= 1;
}
return ans;
}
void Input() {
std::cin >> n >> m >> r >> k;
for(int i = 1,u,v,w;i <= k; i++) {
std::cin >> u >> v >> w;
Add(u,v,w);
in[v] ++;
out[u] ++;
sum += w;
sum %= MOD;
}
return ;
}
void Ready() {
inverse = pow(sum,MOD - 2);
std::cerr << inverse <<"\n";
inv[1] = 1;
for(int i = 2;i <= k; i++)
inv[i] = (long long)(MOD - MOD / i) * inv[MOD % i] % MOD;
for(int i = 1;i <= n; i++)
if(!in[i])
que.push(i);
for(int i = 1;i <= m; i++)
f[i][0] = f[i][1] = 1;
return ;
}
void Work() {
while(!que.empty()) {
int x = que.front();
que.pop();
int tot = 0;
for(int i = h[x];i; i = e[i].next) {
int to = e[i].to;
in[to] --;
if(!in[to])
que.push(to);
f[to][1] = (f[to][1] + (long long)f[x][1] % MOD * inv[out[x]] % MOD) % MOD;
f[to][0] = (f[to][0] + (long long)f[x][0] % MOD * inv[out[x]] % MOD) % MOD;
tot = (tot + (long long)f[x][0] % MOD * inverse % MOD * e[i].val % MOD * inv[out[x] - 1] % MOD * inv[out[x]]) % MOD;
}
for(int i = h[x];i; i = e[i].next) {
int to = e[i].to;
f[to][1] = (f[to][1] + tot - (long long)f[x][0] % MOD * inverse % MOD * e[i].val % MOD * inv[out[x] - 1] % MOD * inv[out[x]] % MOD + MOD) % MOD;
f[to][1] = (f[to][1] + MOD - (long long)f[x][0] % MOD * inverse % MOD * e[i].val % MOD * inv[out[x]] % MOD) % MOD;
}
}
return ;
}
void Output() {
for(int i = n - r + 1;i <= n; i++)
std::cout << (f[i][1] + MOD) % MOD << " ";
return ;
}
signed main() {
Input();
Ready();
Work();
Output();
return 0;
}