E.Lunar New Year and Red Envelopes
key
- DP
- 并查集?
题意
有 n n n个时间点, k k k个红包。
每个红包有几个参数:可以在 l l l到 r r r时间点拿这个红包,拿了之后就只能等到 d + 1 d+1 d+1时刻再拿,这个红包有 c c c块钱。
Bob吸金的策略:每当他可以拿钱,他就选能选的红包中 c c c最大的,如果有多个一样的 c c c,就选 d d d最大的, d d d还一样随便挑一个(反正没有影响)。
Alice可以挑 m m m个时间点不让Bob拿红包,但Bob永远按照上述策略吸金。求Bob最少能拿多少钱。
思路
看出这是DP。
f [ i ] [ j ] f[i][j] f[i][j]表示从 i i i时间开始到结束,用掉 j j j次干扰,能得到的最少钱。 i d [ i ] id[i] id[i]表示不管前面的影响,第 i i i个时刻会拿哪个红包。得到递推式:
f [ i ] [ j ] = m i n ( f [ i + 1 ] [ j − 1 ] , f [ d [ i d [ i ] ] + 1 ] [ j ] + c [ i d [ i ] ] ) f[i][j]=min(f[i+1][j-1],f[d[id[i]]+1][j]+c[id[i]]) f[i][j]=min(f[i+1][j−1],f[d[id[i]]+1][j]+c[id[i]])
而 i d [ i ] id[i] id[i]可以用线段树或者并查集预处理。所以总复杂度应该是 O ( k ∗ l o g k + n ∗ m ) O(k*log~k+n*m) O(k∗log k+n∗m)
F. Lunar New Year and a Recursive Sequence
key
- 矩阵快速幂
- 数论
- BSGS
题意
有一个数列 f f f,知道前 k − 1 k-1 k−1项全都是 1 1 1,也知道 f n f_n fn是 m m m,知道模数是 998244353 998244353 998244353(质数, g = 3 g=3 g=3),还知道递推式如下:
f i = ∏ j = 1 k f i − j b i f_i=\prod_{j=1}^{k}f_{i-j}^{b_i} fi=j=1∏kfi−jbi
求 f k f_k fk。
思路
首先知道 f 1... k f_{1...k} f1...k分别是 f k f_k fk的 0...0 , 1 0...0,1 0...0,1次。因为
a b 1 c 1 ∗ a b 2 c 2 ∗ . . . = a b 1 ∗ c 1 + b 2 ∗ c 2 + . . . {a^{b_1}}^{c_1}*{a^{b_2}}^{c_{2}}*...=a^{b_1*c_1+b_2*c_2+...} ab1c1∗ab2c2∗...=ab1∗c1+b2∗c2+...
所以将乘法转换成加法,乘方转换成乘法,可以用矩阵快速幂求出 f n f_n fn是 f k f_k fk的多少次。
然后问题变成了求下面式子中的 x x x:
x s ≡ m ( m o d p ) x^s \equiv m (mod~p) xs≡m(mod p)
事实上看题解之前我百度都没有找到这个方程的解法。
题解是这么说的:假设
g
I
(
x
)
≡
x
(
m
o
d
p
)
g^{I(x)} \equiv x(mod~p)
gI(x)≡x(mod p),那么知道
x
x
x可以用BSGS求
I
(
x
)
I(x)
I(x),知道
I
(
x
)
I(x)
I(x)可以用快速幂求
x
x
x。那么式子变成了下面那个样子:
EE
g
I
(
x
)
∗
s
≡
g
I
(
m
)
(
m
o
d
p
)
g^{I(x)*s} \equiv g^{I(m)}(mod~p)
gI(x)∗s≡gI(m)(mod p)
然后因为原根的性质
I ( x ) ∗ s ≡ I ( m ) ( m o d p − 1 ) ( 2 ) I(x)*s \equiv I(m)(mod~p-1)~~(2) I(x)∗s≡I(m)(mod p−1) (2)
上面 ( 2 ) (2) (2)式用 e x _ g c d ex\_gcd ex_gcd来做比较方便。
所以这题就做出来了!囊括了很多很多数论方面的知识,再加上矩阵快速幂,实在是好题啊!
代码
E
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+10;
const int M = 210;
const int K = 1e5+10;
const int inf = 1e18+10;
int n, m, k;
struct NODE{
int l, r, c, d;
void read(){
scanf("%I64d%I64d%I64d%I64d", &l, &r, &d, &c);
}
bool operator < (const NODE &u){
if (c != u.c) return c > u.c;
return d > u.d;
}
}a[K];
int nxt[N], id[N], f[N][M], ans;
int Find(int x)
{
if (x == nxt[x]) return x;
return nxt[x] = Find(nxt[x]);
}
void Init()
{
scanf("%I64d%I64d%I64d", &n, &m, &k);
for (int i = 1; i <= k; ++ i)
a[i].read();
sort(a+1, a+k+1);
for (int i = 1; i <= n+1; ++ i)
nxt[i] = i;
memset(id, -1, sizeof(id));
for (int i = 1; i <= k; ++ i){
for (int j = Find(a[i].l); j <= a[i].r; j = Find(j)){
id[j] = i;
nxt[j] = j+1;
}
}
}
void DP()
{
memset(f, 0x3f, sizeof(f));
f[n+1][0] = 0;
for (int i = n; i >= 1; -- i)
for (int j = 0; j <= m; ++ j){
if (j) f[i][j] = min(f[i][j], f[i+1][j-1]);
if (id[i] != -1) f[i][j] = min(f[i][j], f[a[id[i]].d+1][j]+a[id[i]].c);
else f[i][j] = min(f[i][j], f[i+1][j]);
}
ans = inf;
for (int j = 0; j <= m; ++ j)
ans = min(ans, f[1][j]);
printf("%I64d\n", ans);
}
signed main()
{
Init();
DP();
return 0;
}
F
//略微有点乱,因为思想经过多次挣扎
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
const int g = 3;
const int K = 210;
const int phi = 402653184;
// phi(p-1),没有用,本来想用快速幂算s^-1(mod p-1),后来发现ex_gcd更方便,还可以顺便判掉不可能的情况
int k, n, m, b[K], s, qm, qx, ans;
struct MAT{
int a[K][K];
void clear(){
memset(a, 0, sizeof(a));
}
void set1(){
memset(a, 0, sizeof(a));
for (int i = 1; i <= k; ++ i)
a[i][i] = 1;
}
}a, t;
map<int, int> mp;
void Init()
{
scanf("%d", &k);
for (int i = 1; i <= k; ++ i)
scanf("%d", &b[i]);
scanf("%d%d", &n, &m);
}
void Add(int &x, int y, int p){if ((x += y) >= p) x -= p;}
MAT Mul(MAT x, MAT y)
{
MAT z;
z.clear();
for (int o = 1; o <= k; ++ o)
for (int i = 1; i <= k; ++ i)
for (int j = 1; j <= k; ++ j)
Add(z.a[i][j], 1ll*x.a[i][o]*y.a[o][j]%(mod-1), mod-1);
return z;
}
MAT Pow(MAT x, int y)
{
MAT z;
z.set1();
while (y){
if (y&1) z = Mul(z, x);
x = Mul(x, x);
y >>= 1;
}
return z;
}
void Solve_s()
{
a.clear();
a.a[1][1] = 1;
t.clear();
for (int i = 1; i <= k; ++ i)
t.a[i][1] = b[i];
for (int i = 2; i <= k; ++ i)
t.a[i-1][i] = 1;
a = Mul(a, Pow(t, n-k));
s = a.a[1][1];
}
int Pow_int(int x, int y, int p)
{
int z = 1;
while (y){
if (y&1) z = 1ll*z*x%p;
x = 1ll*x*x%p;
y >>= 1;
}
return z;
}
int ex_gcd(int a, int b, int &x, int &y)
{
if (b == 0){
x = 1;
y = 0;
return a;
}
int _x, _y, d;
d = ex_gcd(b, a%b, _x, _y);
x = _y;
y = _x-a/b*_y;
return d;
}
void Solve_ans()
{
if (s == 0){
if (m == 1) puts("1");
else puts("-1");
exit(0);
}
int sqr = ceil(sqrt(mod)), mul = 1, mul2;
mp.clear();
for (int i = 1; i <= sqr; ++ i){
mul = 1ll*mul*g%mod;
mp[mul] = i;
}
mul2 = Pow_int(m, mod-2, mod);
for (int i = 1; i <= sqr; ++ i){
mul2 = 1ll*mul2*mul%mod;
if (mp.find(mul2) != mp.end()){
qm = sqr*i-mp[mul2];
break;
}
}
int tmp, qx;
int d = ex_gcd(s, mod-1, qx, tmp);
if (qm%d != 0){
puts("-1");
exit(0);
}
qx = (1ll*qx*qm/d%(mod-1)+mod-1)%(mod-1);
ans = Pow_int(g, qx, mod);
printf("%d\n", ans);
}
int main()
{
Init();
Solve_s();
Solve_ans();
return 0;
}