省队集训Ⅱ-Day8
Day8
Color
这道题的优化是真的爽, 考场上的代码我存了
开始的开始, 写了二维的 DP,
color-40'.cpp
然后考虑用
然后发现如果要得到正确的答案, RE, 只能把坐标向右平移
对于每个
对于点
写了非常麻烦的版本, 标称 WA 掉了, 应该是因为我的思路太繁杂, 有一点细节没有处理好.
总复杂度
unsigned n, m, Ans(0), f[1005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return this->Quek < x.Quek;
}
}Q[100005];
int main() {
n = RD(), m = RD();
for (register unsigned i(1); i <= m; ++i) {
Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
}
sort(Q + 1, Q + m + 1);
for (register unsigned T(1); T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
memset(f, 0, sizeof(f));
f[1] = 1;
for (register unsigned i(Q[T].Quek); i <= n + Q[T].Quek; ++i) {
for (register unsigned j(1); j <= i - Q[T].Quek; ++j) {
f[i] += f[j];
if(f[i] >= MOD) f[i] -= MOD;
}
}
}
register unsigned long long TmpA(1);
for (register unsigned i(1 + Q[T].Quek); i <= n + Q[T].Quek - Q[T].Quex; ++i) {
TmpA += f[i];
if(TmpA >= MOD) TmpA -= MOD;
}
for (register unsigned i(1 + Q[T].Quek); i < Q[T].Quex + Q[T].Quek; ++i) {
TmpA += f[i];
if(TmpA >= MOD) TmpA -= MOD;
}
for (register unsigned i(1 + Q[T].Quek); i < Q[T].Quex + Q[T].Quek; ++i) {
for (register unsigned j(1 + Q[T].Quek); j <= min(n + Q[T].Quek - Q[T].Quex, n + Q[T].Quek + 1 - i); ++j) {
TmpA = (TmpA + (unsigned long long)f[i] * f[j]) % MOD;
}
}
Q[Q[T].Num].Ans = TmpA;
}
for (register unsigned i(1); i <= m; ++i) {
printf("%u\n", Q[i].Ans);
}
return 0;
}
color-50'Ex.cpp
这个版本已经能过 40' 了, 显然我对我的时间复杂度没有准确的估计.
这个版本的更新是设
unsigned n, m, Ans(0), f[100005], Sum[100005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return (this->Quek ^ x.Quek) ? (this->Quek < x.Quek) : (this->Quex < x.Quex);
}
}Q[100005];
int main() {
n = RD(), m = RD();
for (register unsigned i(1); i <= m; ++i) {
Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
}
sort(Q + 1, Q + m + 1);
for (register unsigned T(1); T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
memset(f, 0, sizeof(f)), f[1] = 1, Sum[0] = 0;
for (register unsigned i(Q[T].Quek), j(1), TmpFj(0); i <= n + Q[T].Quek; ++i) {
while (j <= i - Q[T].Quek) {TmpFj += f[j], ++j; if(TmpFj >= MOD) TmpFj -= MOD;}
f[i] += TmpFj; if(f[i] >= MOD) f[i] -= MOD;
}
for (register unsigned i(1); i <= n + Q[T].Quek; ++i) {Sum[i] = Sum[i - 1] + f[i]; if(Sum[i] >= MOD) Sum[i] -= MOD;}
}
register unsigned long long TmpA(1);//都不选
TmpA += Sum[n + Q[T].Quek - Q[T].Quex] - 1;
if(TmpA >= MOD) TmpA -= MOD;
TmpA += Sum[Q[T].Quex + Q[T].Quek - 1] - 1;
if(TmpA >= MOD) TmpA -= MOD;
for (register unsigned i(Q[T].Quex + Q[T].Quek - 1), TmpFj(0), j(1 + Q[T].Quek); i > Q[T].Quek; --i) {
while (j <= min(n + Q[T].Quek - Q[T].Quex, n + Q[T].Quek + 1 - i)) {TmpFj += f[j],++j; if(TmpFj >= MOD) TmpFj -= MOD;}
TmpA = (TmpA + (unsigned long long)f[i] * TmpFj) % MOD;
}
Q[Q[T].Num].Ans = TmpA;
}
for (register unsigned i(1); i <= m; ++i) {
printf("%u\n", Q[i].Ans);
}
return 0;
}
color-50'Ex_Pro.cpp
很遗憾, 这个版本还是
重新审视我们的转移方程:
我们发现, 因为
方程的正确性是这样保证的: 原来的
所以这次更新是对代码难度的更新, 之前总是算下标恶心死人.
unsigned n, m, Ans(0), f[100005], Sum[100005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return (this->Quek ^ x.Quek) ? (this->Quek > x.Quek) : (this->Quex < x.Quex);
}
}Q[100005];
int main() {
n = RD(), m = RD();
for (register unsigned i(1); i <= m; ++i) {
Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
}
sort(Q + 1, Q + m + 1);
for (register unsigned T(1); T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
memset(f, 0, sizeof(f)), Sum[0] = 0;
for (register unsigned i(1), j(1), TmpFj(1); i <= n; ++i) {
while (j + Q[T].Quek <= i) {TmpFj += f[j], ++j; if(TmpFj >= MOD) TmpFj -= MOD;}
f[i] = TmpFj;
}
for (register unsigned i(1); i <= n; ++i) {Sum[i] = Sum[i - 1] + f[i]; if(Sum[i] >= MOD) Sum[i] -= MOD;}
}
register unsigned long long TmpA(1);//都不选
TmpA += Sum[n - Q[T].Quex];//不选左
if(TmpA >= MOD) TmpA -= MOD;
TmpA += Sum[Q[T].Quex - 1];//不选右
if(TmpA >= MOD) TmpA -= MOD;
for (register unsigned i(Q[T].Quex - 1), j(1), AddDown; i; --i) {
AddDown = n - max(Q[T].Quex, i + Q[T].Quek - 1);
if(AddDown > 0x3f3f3f3f) {AddDown = 0;}
TmpA = (TmpA + (unsigned long long)f[i] * Sum[AddDown]) % MOD;
}
Q[Q[T].Num].Ans = TmpA;
}
for (register unsigned i(1); i <= m; ++i) printf("%u\n", Q[i].Ans);
return 0;
}
color-50'Ex_Pro_Max.cpp
这个版本无愧它 Pro Max 的称号, 得了
unsigned n, m, Ans(0), f[100005], Sum[100005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return (this->Quek ^ x.Quek) ? (this->Quek < x.Quek) : (this->Quex < x.Quex);
}
}Q[100005];
int main() {
n = RD(), m = RD();
for (register unsigned i(1); i <= m; ++i) {
Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
}
sort(Q + 1, Q + m + 1);
for (register unsigned T(1); T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
memset(f, 0, sizeof(f)), Sum[0] = 0;
for (register unsigned i(1), j(1), TmpFj(1); i <= n; ++i) {
while (j + Q[T].Quek <= i) {TmpFj += f[j], ++j; if(TmpFj >= MOD) TmpFj -= MOD;}
f[i] = TmpFj;
}
for (register unsigned i(1); i <= n; ++i) {Sum[i] = Sum[i - 1] + f[i]; if(Sum[i] >= MOD) Sum[i] -= MOD;}
}
register unsigned long long TmpA(1);//都不选
TmpA += Sum[n - Q[T].Quex];//不选左
if(TmpA >= MOD) TmpA -= MOD;
TmpA += Sum[Q[T].Quex - 1];//不选右
if(TmpA >= MOD) TmpA -= MOD;
if(Q[T].Quex + 1 > Q[T].Quek) {
if(Q[T].Quek < 2) TmpA = (TmpA + (unsigned long long)Sum[Q[T].Quex - 1] * Sum[n - Q[T].Quex]) % MOD;
else {
TmpA = (TmpA + (unsigned long long)(Sum[Q[T].Quex - Q[T].Quek + 1]) * Sum[n - Q[T].Quex]) % MOD;
for (register unsigned i(Q[T].Quex - 1), AddDown; i > Q[T].Quex - Q[T].Quek + 1; --i) {
AddDown = n + 1 - i - Q[T].Quek;
if(AddDown > 0x3f3f3f3f) AddDown = 0;
TmpA = (TmpA + (unsigned long long)f[i] * Sum[AddDown]) % MOD;
}
}
} else {
for (register unsigned i(Q[T].Quex - 1), AddDown; i; --i) {
AddDown = n - max(Q[T].Quex, i + Q[T].Quek - 1);
if(AddDown > 0x3f3f3f3f) AddDown = 0;
TmpA = (TmpA + (unsigned long long)f[i] * Sum[AddDown]) % MOD;
}
}
Q[Q[T].Num].Ans = TmpA;
}
for (register unsigned i(1); i <= m; ++i) printf("%u\n", Q[i].Ans);
return 0;
}
color-70'Ex_Pro_Max_Ti.cpp
这个 70' 非常的丢人, 因为它只有
优化力度不大, 代码方面减少了讨论, 将两种情况合为一种, 因为两种唯一的区别是枚举
unsigned n, m, Ans(0), f[100005], Sum[100005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return (this->Quek ^ x.Quek) ? (this->Quek < x.Quek) : (this->Quex < x.Quex);
}
}Q[100005];
int main() {
n = RD(), m = RD();
for (register unsigned i(1); i <= m; ++i) Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
sort(Q + 1, Q + m + 1);
for (register unsigned T(1); T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
memset(f, 0, sizeof(f)), Sum[0] = 0;
for (register unsigned i(1), j(1), TmpFj(1); i <= n; ++i) {
while (j + Q[T].Quek <= i) {TmpFj += f[j], ++j; if(TmpFj >= MOD) TmpFj -= MOD;}
f[i] = TmpFj;
}
for (register unsigned i(1); i <= n; ++i) {Sum[i] = Sum[i - 1] + f[i]; if(Sum[i] >= MOD) Sum[i] -= MOD;}
}
register unsigned long long TmpA(1);//都不选
register unsigned Flor(min(Q[T].Quex - 1, n + 1 - Q[T].Quek)), Ceil(0);
TmpA += Sum[n - Q[T].Quex];//不选左
if(TmpA >= MOD) TmpA -= MOD;
TmpA += Sum[Q[T].Quex - 1];//不选右
if(TmpA >= MOD) TmpA -= MOD;
if(Q[T].Quex + 1 > Q[T].Quek)
Ceil = Q[T].Quex - Q[T].Quek + 1, TmpA = (TmpA + (unsigned long long)Sum[Q[T].Quex - max(Q[T].Quek - 1, _1)] * Sum[n - Q[T].Quex]) % MOD;
for (register unsigned i(Flor), AddDown(n + 1 - i - Q[T].Quek); i > Ceil; --i, ++AddDown) TmpA = (TmpA + (unsigned long long)f[i] * Sum[AddDown]) % MOD;
Q[Q[T].Num].Ans = TmpA;
}
for (register unsigned i(1); i <= m; ++i) printf("%u\n", Q[i].Ans);
return 0;
}
color-70'Ex_Pro_Max_Ti_X.cpp
重新审视这个询问的过程, 发现所有的方案数
所以变成了
unsigned n, m, Ans(0), f[100005], Sum[100005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return (this->Quek ^ x.Quek) ? (this->Quek < x.Quek) : (this->Quex < x.Quex);
}
}Q[100005];
int main() {
n = RD(), m = RD();
for (register unsigned i(1); i <= m; ++i)
Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
sort(Q + 1, Q + m + 1);
for (register unsigned T(1); T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
memset(f, 0, sizeof(f)), Sum[0] = 0;
for (register unsigned i(1), j(1), TmpFj(1); i <= n; ++i) {
while (j + Q[T].Quek <= i) {TmpFj += f[j], ++j; if(TmpFj >= MOD) TmpFj -= MOD;}
f[i] = TmpFj;
}
for (register unsigned i(1); i <= n; ++i) {Sum[i] = Sum[i - 1] + f[i]; if(Sum[i] >= MOD) Sum[i] -= MOD;}
}
Q[Q[T].Num].Ans = (MOD + (Sum[n] + 1) - ((unsigned long long)f[Q[T].Quex] * f[n - Q[T].Quex + 1] % MOD));
if(Q[Q[T].Num].Ans >= MOD) Q[Q[T].Num].Ans -= MOD;
}
for (register unsigned i(1); i <= m; ++i) printf("%u\n", Q[i].Ans);
return 0;
}
color-70'Ex_Pro_Max_Ti_X_S.cpp
这个 70' 实测得了 -O2) 也是全场唯一的
这是我考试时最后一个版本, 这份代码主要是对 DP 的过程进行剪枝, 因为打表可得, 对于某个
unsigned n, m, Ans(0), f[100005], Sum[100005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return (this->Quek ^ x.Quek) ? (this->Quek < x.Quek) : (this->Quex < x.Quex);
}
}Q[100005];
int main() {
n = RD(), m = RD();
for (register unsigned i(1); i <= m; ++i)
Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
sort(Q + 1, Q + m + 1);
for (register unsigned T(1); T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
for (register unsigned i(Q[T - 1].Quek + 1); i <= Q[T].Quek; ++i) f[i] = 1, Sum[i] = i;
for (register unsigned i(Q[T].Quek + 1); i <= n; ++i) {
f[i] = Sum[i - Q[T].Quek] + 1;
Sum[i] = Sum[i - 1] + f[i]; if(Sum[i] >= MOD) Sum[i] -= MOD;
}
}
Q[Q[T].Num].Ans = (MOD + (Sum[n] + 1) - ((unsigned long long)f[Q[T].Quex] * f[n - Q[T].Quex + 1] % MOD));
if(Q[Q[T].Num].Ans >= MOD) Q[Q[T].Num].Ans -= MOD;
}
for (register unsigned i(1); i <= m; ++i) printf("%u\n", Q[i].Ans);
return 0;
}
color-100'Ex_Pro_Max_Ti_X_Ultra_Extreme.cpp
在写正解之前, 先来看一下以张业琛为代表的选手的
我们知道一个黑点前面一定有
考虑有
对于
接下来考虑
求
也就是说, 对于询问
只要可以
众所周知, 二项式系数:
我们只要预处理出
要预处理
假设
则
这样, 总的复杂度就是
接下来考虑正解.
我们现在有算法可以做到
最后是代码:
unsigned n, m, Ans(0), f[100005], Sum[100005], T(1), Sq;
unsigned long long Inv[200005], Fac[200005];
char flg(0);
struct Que {
unsigned Quex, Quek, Num, Ans;
inline const char operator<(const Que &x) const{
return (this->Quek ^ x.Quek) ? (this->Quek < x.Quek) : (this->Quex < x.Quex);
}
}Q[100005];
int main() {
n = RD(), m = RD(), Sq = sqrt(n);
for (register unsigned i(1); i <= m; ++i) Q[i].Quex = RD(), Q[i].Quek = RD(), Q[i].Num = i;
sort(Q + 1, Q + m + 1), Fac[0] = 1;
for (register unsigned i(1); i <= n + Q[m].Quek; ++i) Fac[i] = Fac[i - 1] * i % MOD;
register unsigned TmpM(MOD - 2);
register unsigned long long TmpFac(Fac[n + Q[m].Quek]);
Inv[n + Q[m].Quek] = 1;
while (TmpM) {
if(TmpM & 1) Inv[n + Q[m].Quek] = Inv[n + Q[m].Quek] * TmpFac % MOD;
TmpFac = TmpFac * TmpFac % MOD, TmpM >>= 1;}
for (register unsigned i(n + Q[m].Quek - 1); i < 0x3f3f3f3f; --i) Inv[i] = Inv[i + 1] * (i + 1) % MOD;
for (; Q[T].Quek <= Sq && T <= m; ++T) {
if(Q[T].Quek ^ Q[T - 1].Quek){
for (register unsigned i(Q[T - 1].Quek + 1); i <= Q[T].Quek; ++i) f[i] = 1, Sum[i] = i;
for (register unsigned i(Q[T].Quek + 1); i <= n; ++i){
f[i] = Sum[i - Q[T].Quek] + 1, Sum[i] = Sum[i - 1] + f[i]; if(Sum[i] >= MOD) Sum[i] -= MOD;}
}
Q[Q[T].Num].Ans = (MOD + (Sum[n] + 1) - ((unsigned long long)f[Q[T].Quex] * f[n - Q[T].Quex + 1] % MOD));
if(Q[Q[T].Num].Ans >= MOD) Q[Q[T].Num].Ans -= MOD;
}
for (register unsigned long long TmpTotal(1), TmpLe(1), TmpRi(1); T <= m; ++T) {
for (register unsigned i((n + Q[T].Quek - 1)/ Q[T].Quek), Len(n + Q[T].Quek - 1 - Q[T].Quek * i + i); i; --i, Len += (Q[T].Quek - 1))
TmpTotal = (TmpTotal + (Fac[Len] * Inv[i] % MOD) * Inv[Len - i]) % MOD;
for (register unsigned i((n - Q[T].Quex)/ Q[T].Quek), Len(n - Q[T].Quex - Q[T].Quek * i + i); i; --i, Len += (Q[T].Quek - 1))
TmpLe = (TmpLe + (Fac[Len] * Inv[i] % MOD) * Inv[Len - i]) % MOD;
for (register unsigned i((Q[T].Quex - 1) / Q[T].Quek), Len(Q[T].Quex - 1 - Q[T].Quek * i + i); i; --i, Len += (Q[T].Quek - 1))
TmpRi = (TmpRi + (Fac[Len] * Inv[i] % MOD) * Inv[Len - i]) % MOD;
Q[Q[T].Num].Ans = MOD + TmpTotal - (TmpLe * TmpRi % MOD), TmpTotal = TmpLe = TmpRi = 1;
if(Q[Q[T].Num].Ans >= MOD) Q[Q[T].Num].Ans -= MOD;
}
for (register unsigned i(1); i <= m; ++i) printf("%u\n", Q[i].Ans);
return 0;
}