codeforces 1106 (div2) E F

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][j1],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(klog k+nm)

F. Lunar New Year and a Recursive Sequence

key

  • 矩阵快速幂
  • 数论
  • BSGS

题意

有一个数列 f f f,知道前 k − 1 k-1 k1项全都是 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=1kfijbi

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+...} ab1c1ab2c2...=ab1c1+b2c2+...

所以将乘法转换成加法,乘方转换成乘法,可以用矩阵快速幂求出 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) xsm(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)sgI(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)sI(m)(mod p1)  (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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值