CF Global Round 21 题解 (CDEG)

C

a,b 全拆开然后比较即可(因为分裂和合并是互逆的)

注意开 long long .

using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
int n, m, k;
vector<pii> a, b, c, d;
inline void solve()
{
	a.clear(); b.clear(); c.clear(); d.clear();
	scanf("%d%d", &n, &k); 
	for (int i=1, x; i<=n; i++)
	{
		scanf("%d", &x);
		int c = x;
		while (!(c % k)) c /= k;
		int cc = x / c;
		a.emplace_back(make_pair(c, cc));
	}
	int l = a.size();
	for (int i=1; i<l; i++)
		if (a[i].first == a[i-1].first){a[i].second += a[i-1].second; a[i-1].second = 0;}
	for (pii x : a) if (x.second) c.emplace_back(x); 
	scanf("%d", &m);
	for (int i=1, x; i<=m; i++)
	{
		scanf("%d", &x);
		int c = x;
		while (!(c % k)) c /= k;
		int cc = x / c;
		b.emplace_back(make_pair(c, cc));
	}
	l = b.size();
	for (int i=1; i<l; i++)
		if (b[i].first == b[i-1].first){b[i].second += b[i-1].second; b[i-1].second = 0;}
	for (pii x : b) if (x.second) d.emplace_back(x); 
	if (c == d) puts("Yes");
	else puts("No");
}
int main()
{
	int T; scanf("%d", &T);
	while (T--) solve();
	return 0;
}

D

两种想法 .

第一种是类似笛卡尔树,显而易见一段区间内的最大值和最小值必然被经过,于是分治即可,O(n) .

第二种是贪心,令 ri 表示最大的 p 满足 t(i,p],at<ai,这个可以直接用一个栈维护或者二分 + ST 表 .

然后每个 i 的转移点必然是 argminj(i,ri]{aj}(cnblogs 竟然不支持 \argmin,直接 ST 表找即可 .

这样的时间复杂度是 O(nlogn) .

然而 EI 大师写了一份暴力跳的就过了 /yun 复杂度竟然是对的 .

魔改 EI 版:

using namespace std;
constexpr int N = 3e5 + 233, P = 1e9 + 7;
typedef pair<int, int> pii;
typedef long long ll;
int n, a[N], st[N], nxtL[N], nxtS[N]; // st --> stack
inline void solve()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", a+i);
	int sz = 0; st[0] = n+1;
	for (int i=n; i; i--)
	{
		while (sz && (a[st[sz]] < a[i])) --sz;
		nxtL[i] = st[sz];
		st[++sz] = i;
	}
	sz = 0;
	for (int i=n; i; i--)
	{
		while (sz && (a[st[sz]] > a[i])) --sz;
		nxtS[i] = st[sz];
		st[++sz] = i;
	}
	int p = 1, step = 0, nxt = 114514; 
	while (p < n)
	{
		if (a[p] < a[p+1])
		{
			nxt = p;
			while (nxtL[nxt] < nxtS[p]) nxt = nxtL[nxt];
			p = nxt;
		}
		else
		{
			nxt = p;
			while (nxtS[nxt] < nxtL[p]) nxt = nxtS[nxt];
			p = nxt;
		}
		++step;
	} printf("%d\n", step);
}
int main()
{
	int T; scanf("%d", &T);
	while (T--) solve();
	return 0;
}

E

首先读题得答案是

ans=i=0nj=0ai1(i+jj)

根据平行求和法可得

ans=i=0n(i+aii+1)

预处理逆元直接算即可,时间复杂度 O(n+maxi{ai}) .


一些说明:

Fi,j 表示在 (i,j) 放一个关键点最少操作多少次能把关键点移出白格,于是有转移

Fi,j=Fi+1,j+Fi,j+1

可以转换为格路计数问题,于是 Fi,j 就变成了 (0,0)(i,j) 的路径数量 .

于是 Fi,j=(i+jj) .

答案即 i=0nj=0ai1Fi,j .

using namespace std;
const int N = 4e5 + 233, P = 1e9 + 7;
typedef long long ll;
typedef pair<ll, ll> pii;
int n, a[N], fac[N], infac[N];
int qpow(int a, int n)
{
    int ans = 1;
    while (n)
    {
        if (n & 1) ans = 1ll * ans * a % P;
        a = 1ll * a * a % P; n >>= 1;
    } return ans;
}
void init()
{
	fac[0] = 1;
	for (int i=1; i<N; i++) fac[i] = 1ll * fac[i-1] * i % P;
	infac[N-1] = qpow(fac[N-1], P-2);
	for (int i=N-2; i>=0; i--) infac[i] = 1ll * infac[i+1] * (i+1) % P;
}
int binom(int n, int m)
{
	if (n < m) return 0;
	return 1ll * fac[n] * infac[m] % P * infac[n-m] % P;
}
int main()
{
	init(); scanf("%d", &n);
	for (int i=0; i<=n; i++) scanf("%d", a+i);
	int ans = 0;
	for (int i=0; i<=n; i++) (ans += binom(i + a[i], i + 1)) %= P;
	printf("%d\n", ans);
	return 0;
}

G

设原序列为 {A} .

f(A) 可以表示为线性规划(a0=b0=0

minimizei=1n(ai+bi)s.t.{i[1,n],xai+ybi+xbi1+yai1Aiyan1+xbn1ani[1,n],ai0,bi0

的解 .

对偶,得

maximizei=1nAixis.t.{i[1,n),xxi+yxi+11i[1,n),yxi+xxi+11i[1,n],xi0

可以证明 xi 的取值只可能是 1max(x,y),1x+y,0 三种(具体看官方题解).

于是可以分类 DP,令 dpi,0/1/2 表示 1i 的最优解,其中 xi 取可以取到的第 0/1/2 种值 .

预处理出转移矩阵,多组询问只需要线段树维护即可 .

时间复杂度 O(n+qlogn) .

using namespace std;
const int N = 2e5 + 233;
const double INF = 1e18;
typedef long long ll;
typedef pair<ll, ll> pii;
int n, q, x, y, a[N];
struct Node
{
	double dp[3][3];
	Node(double n = 0)
	{
		for (int i=0; i<3; i++)
			for (int j=0; j<3; j++)
				if (i + j <= 2) dp[i][j] = (j ? 1. * n / ((j == 1) ? x + y : y) : 0);
				else dp[i][j] = -INF;
	}
	Node operator + (const Node& rhs) const
	{
		Node ans;
		for (int i=0; i<3; i++)
			for (int j=0; j<3; j++)
			{
				ans.dp[i][j] = -INF;
				for (int k=0; k<3; k++) chkmax(ans.dp[i][j], dp[i][k] + rhs.dp[k][j]);
			}
		return ans;
	}
};
struct SMT
{
#define ls (u << 1)
#define rs (u << 1 | 1)
	struct{int l, r; Node s;}tr[4*N];
	inline void pushup(int u){tr[u].s = tr[ls].s + tr[rs].s;}
	inline void build(int u, int l, int r)
	{
		tr[u].l = l; tr[u].r = r;
		if (l == r){tr[u].s = a[l]; return ;}
		int mid = (l + r) >> 1;
		build(ls, l, mid); build(rs, mid+1, r);
		pushup(u);
	}
	inline void change(int u, int pos, const Node& v)
	{
		if (tr[u].l == tr[u].r){tr[u].s = v; return ;}
		int mid = (tr[u].l + tr[u].r) >> 1;
		if (pos <= mid) change(ls, pos, v);
		else change(rs, pos, v);
		pushup(u);
	}
	inline Node query(int u, int l, int r)
	{
		int L = tr[u].l, R = tr[u].r;
		if ((l > R) || (L > r)) return Node(); 
		if ((l <= L) && (R <= r)) return tr[u].s;
		return query(ls, l, r) + query(rs, l, r); // 不用单位元 & + 没有交换律
	}
#undef rs
#undef ls
}T;
int main()
{
	scanf("%d%d%d%d", &n, &q, &x, &y);
	if (x > y) swap(x, y);
	for (int i=1; i<=n; i++) scanf("%d", a+i);
	T.build(1, 1, n);
	int opt, x, y;
	while (q--)
	{
		scanf("%d%d%d", &opt, &x, &y);
		if (opt == 1) T.change(1, x, y);
		else printf("%.15f\n", (T.query(1, x, y) + Node()).dp[0][0]);
	} return 0;
}
posted @   yspm  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
😅​
点击右上角即可分享
微信分享提示