Rank
打成大奋了
A. 选彩笔(rgb)
签 我是彩笔
赛时完全不会啊,打了一个 25k 的贪心结果爆栈了喜提 0pts。
最大值最小,还是二分答案。二分的答案是最大差,发现值域很小,我们在 check 时可以直接枚举每个色号的最大值,统计在所选区间范围内笔数。这样就有了 \(\mathcal{O(w^3n\log w)}\) 的暴力。
考虑三维前缀和优化,\(\mathcal{O(w^3)}\) 预处理,容斥求值,然后可以在 \(\mathcal{O(w^3\log w)}\) 的复杂度下过掉了。理论算着可能会 TLE,不过不需要太卡常也能过。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e5 + 5;
int n, m, ans;
struct rmm{int r, g, b, id;} p[N];
int sum[260][260][260];
namespace Wisadel
{ll Wq(int i, int j, int k, int mid){ll res = sum[i][j][k];if(i >= mid){if(j >= mid){if(k >= mid) res -= sum[i - mid][j - mid][k - mid];res += sum[i - mid][j - mid][k];}if(k >= mid) res += sum[i - mid][j][k - mid];res -= sum[i - mid][j][k];}if(j >= mid){if(k >= mid) res += sum[i][j - mid][k - mid];res -= sum[i][j - mid][k];}if(k >= mid) res -= sum[i][j][k - mid];return res;}bool Wck(int tar){fo(i, tar - 1, 255) fo(j, tar - 1, 255) fo(k, tar - 1, 255)if(Wq(i, j, k, tar) >= m) return 1;return 0;}short main(){freopen("rgb.in", "r", stdin), freopen("rgb.out", "w", stdout);n = qr, m = qr;fo(i, 1, n) p[i].r = qr, p[i].g = qr, p[i].b = qr, sum[p[i].r][p[i].g][p[i].b]++;fo(i, 1, 255) fo(j, 0, 255) fo(k, 0, 255) sum[i][j][k] += sum[i - 1][j][k];fo(i, 0, 255) fo(j, 1, 255) fo(k, 0, 255) sum[i][j][k] += sum[i][j - 1][k];fo(i, 0, 255) fo(j, 0, 255) fo(k, 1, 255) sum[i][j][k] += sum[i][j][k - 1];int l = 0, r = 255, ans = 255;while(l <= r){int mid = (l + r) >> 1;if(Wck(mid + 1)) r = mid - 1, ans = mid;else l = mid + 1;}printf("%d\n", ans);return Ratio;}
}
signed main(){return Wisadel::main();}
B. 兵蚁排序(sort)
也很水的一个题,赛时因为 T1 想假了耽误很长时间所以没多想,推出一个假性质居然能模过所有样例,但是假,所以 30pts。
先说说赛时的假做法:首先因为发现了大的数不可能往前走,所以我是倒着做的,考虑扫到的每一个数:比 \(b_i\) 大,无解,比 \(b_i\) 小,则向前找到第一个 \(b_i\),然后对整个区间排序。你会发现由于是一整块直接排序,中间许多本来符合目标序列关系的数被打乱了,因此会多判许多无解的情况。
那么根据这个错误,推出一个有正确性的做法:正着扫,然后对于当前序列向前扫到第一个等于 \(b_i\) 的值,然后一个一个交换判断是否可行。考虑这样为什么是对的:我们逐个移动保证了移动后序列仍然满足一些原序列的逆序关系,目标序列若也存在这种关系就不管,否则再交换一次即可打破逆序关系,因此这样动不会漏掉情况。
复杂度 \(\mathcal{O(Tn^2)}\)。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define fi first
#define se second
#define M_P(x, y) make_pair(x, y)
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e5 + 5;
int n;
int a[N], b[N];
multiset<int> st;
vector<pii> ans;
namespace Wisadel
{short main(){freopen("sort.in", "r", stdin), freopen("sort.out", "w", stdout);int T = qr;while(T--){n = qr; ans.clear();fo(i, 1, n) a[i] = qr;fo(i, 1, n) b[i] = qr;bool bok = 0;fo(i, 1, n){int zc = 0;fo(j, i, n) if(b[i] == a[j]){zc = j; break;}fu(j, zc, i + 1){if(a[j] < a[j - 1]){ans.P_B(M_P(j - 1, j));swap(a[j], a[j - 1]);}else{bok = 1; break;}}if(bok) break;}if(bok) puts("-1");else{puts("0");printf("%d\n", ans.size());for(auto i : ans) printf("%d %d\n", i.fi, i.se);}}return Ratio;}
}
signed main(){return Wisadel::main();}
C. 人口局 DBA(dba)
推柿子题。
看到的第一反应就是数位 dp,然而看一会你就会发现时间空间都是假的。然后把暴力交上去有 22pts。
考虑如何简化问题。依然从数位 dp 的过程思考,当 limit=0 时,我们此后选数没有任何障碍,也就转化成了一个计数问题:共 \(a\) 位,每位上的总和要求为 \(s\),每位能取的范围为 \(\left[0,m\right)\),求方案数。考虑容斥,钦定这 \(a\) 位中有 \(k\) 位上的数 \(\ge m\),即让 \(k\) 个数 \(+m\),求解组数,此时有 \(\sum_{i=1}^a\ x_i=s-km\),等价于 \(\sum_{i=1}^a\ (x_i+1)=s-km+a\),此时满足 \(x_i+1\ge 1\),由插板法可得此时答案为 \(\binom{s-km+a-1}{a-1}\),那么这个子问题的答案也就有了:\(\sum_{k=0}^a\ (-1)^k\binom{a}{k}\binom{s-km+a-1}{a-1}\)。
我们设 \(f_a(s)=\sum_{k=0}^a\ (-1)^k\binom{a}{k}\binom{s-km+a-1}{a-1}\) 表示在无限制条件下位数为 \(a\) 要求总和为 \(s\) 的方案数,方便后续表示。
此时考虑加上 limit 的限制。由于题目要求 \(x\lt n\),所以其本身取到 \(a_i\) 的情况答案是为 0 的不考虑,那么设这一位为 \(id\) (从高到低),则其答案为 $\sum_{i=0}^{a_{id}-1}\ f_{n-id}(s-i) $。
开推:
我们设 \(x=a_{id}-1,y=s-km,p=n-id-1\),则后面一坨可以表示为 \(\sum_{i=0}^x\ \binom{y-i+p}{p}\)。
结合杨辉三角来表示,可以将其化为:
代会原来的式子,得:
考虑代回原题,我们每次多固定一位等于原位上的数,那么此时答案即为 \(Ans_{n-固定的位数}(tot-sum_{固定的位数-1})\),所以总答案为 \(\sum_{i=1}^n\ Ans_{i}(tot-sum_{i-1})\),其中 \(sum_{i}\) 表示第 \(i\) 位及其高位上的数之和。预处理 \(n\times m\) 以内的阶乘和逆元,总复杂度 \(\mathcal{O(nm+n^2)}\)。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define fi first
#define se second
#define M_P(x, y) make_pair(x, y)
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 2000 + 5;
const int mod = 1e9 + 7;
int n, m;
int a[N], zc[N], tot;
ll jc[N * N], ny[N * N], ans;
namespace Wisadel
{ll Wqp(ll x, int y){ll res = 1;while(y){if(y & 1) res = res * x % mod; x = x * x % mod; y >>= 1;}return res;}ll Wc(int n, int m){if(n < m) return 0;return jc[n] * ny[m] % mod * ny[n - m] % mod;}short main(){freopen("dba.in", "r", stdin), freopen("dba.out", "w", stdout);m = qr, n = qr;fo(i, 1, n) a[i] = qr, tot += a[i];jc[0] = ny[0] = 1;fo(i, 1, n * m) jc[i] = jc[i - 1] * i % mod;ny[n * m] = Wqp(jc[n * m], mod - 2);fu(i, n * m - 1, 1) ny[i] = ny[i + 1] * (i + 1) % mod;fo(i, 1, n){fo(k, 0, n - i){int op = (k & 1) ? -1 : 1;ans = (ans + (1ll * op * Wc(n - i, k) % mod + mod) % mod * ((Wc(tot - k * m + n - i, n - i) - Wc(tot - k * m + n - i - a[i], n - i) + mod) % mod) % mod) % mod;}tot -= a[i];}printf("%lld\n", ans);return Ratio;}
}
signed main(){return Wisadel::main();}
D. 银行的源起(banking)
不会。
赛时想到找重心分开再找重心,然后发现还有居民就不会了,打了 \(\mathcal{O(n^3)}\) 暴力拿 10pts。
末
打的依托,T1 上来没思路就很伤,然后想了一个需要打巨大长代码的假思路更伤,结果是导致本来能切两题的场一题没切,暴力还没拿满。
学到的东西太不牢固导致一遇到需要结合多一点东西的题就发懵,维持有效状态的时间短是很重要的一个原因。
huge 晚上来开小会。内务纪律扯🥚不提,但 noip 真可能是我最后一场作为全职 OIer 的比赛了。不说像初见 OI 时那样一定冲金夺银的豪言壮语,多少也要给自己这一年多的时间一个交代,我到底有没有成长,我的实力究竟如何。挂分再怎么不应该也是挂了,没人会听你本来该能多少分,别人看到的只是最终的结果,当然,你自己真正在意的也是。所以拿出最好的状态,打一场酣畅淋漓的比赛才是最好的结果。不知道说这些能不能对自己有点影响,但是真的到了该拼的时候了,加油!
不知道会不会完结撒花~