首页 > 其他分享 >Codeforces Round #816 (Div. 2)/CodeForces1715

Codeforces Round #816 (Div. 2)/CodeForces1715

时间:2022-08-28 10:23:34浏览次数:97  
标签:ch int Codeforces read while Div CodeForces1715 dp define

CodeForces1715

Crossmarket

解析:

题目大意

有一个 \(n \times m\) 的空间,Stanley 需要从左上角到右下角;Megan 则需要从左下角到右上角。两人可以耗费 \(1\) 能量到达相邻的格子。

为了加快到达的速度, Megan 会在经过的所有格子留下传送门,两人可以耗费 \(1\) 能量通过任意传送门通往任意有传送门的位置。

求两人均到达目的地耗费的最小总能量。


思路:

首先发现 Megan 不可能用传送门,因为这样折返最多让 Stanley 能够早点用传送门,但 Stanley 没走的路其实是让 Megan 走的,总能量并不优,正确的做法是 Megan 直接从左下,经过左上走到右上,而 Stanley 选择 \(n,m\) 中较大的边传送。

答案是 \(n-m-2\) (Megan 的能量)+ \(\min(n,m)\) (Stanley 的能量)。


code

#include <bits/stdc++.h>
using namespace std;
int n, m;
int solve ()
{
    scanf ("%d%d", &n, &m);
    if (n == 1 && m == 1) return 0; 
    if (n < m) swap (n, m);
    return (n + m - 2) + m;
}
signed main()
{
    int t = read ();
    while (t--) printf ("%d\n", solve ());
    return 0;
}

Beautiful Array

解析:

题目大意:

给定四个整数 \(n,k,b,s\),要求构造一个长度为 \(n\) 的序列 \(a\),使得 \(s=\sum\limits_{i=1}^na_i\) 且 \(b=\sum\limits_{i=1}^n\lfloor\frac{a_i}{k}\rfloor\)(\(a_i\) 必须为非负整数)。如果有多组解,输出任意一组解。对于无解的情况,输出 -1


思路:

大力构造,考虑总和 \(s\) 能凑出来的最大的 \(b\) 其实是 \(\frac{s}{k}\) ,感性理解就是我前面 \(n-1\) 个 \(a_i\) 都取 \(k\),最后剩了多少都给 \(a_n\),可以证明不存在更优的构造方案。

总和 \(s\) 可以构造的最小值可以类似考虑,我先给 \(n\) 个 \(a_i\) 都分 \(k-1\),那剩下的数不管放哪都会造成贡献了,那直接随便找一个放在一起就行了,因为我需要尽量不破坏其他 \(k-1\),可以证明这样凑出来的是最小的。

考虑现在判断了无解,构造方案可以参考最小值的构造,我先给 \(a_1\) 分 \(b\times k\),剩下的每次按照 \(k-1\) 分给 \(1-n\) 所有数,直到分完为止。


code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF = 0x3f3f3f3f;
inline int read ()
{
    int x = 0, f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
    return x * f;
}
int n, k, b, s;
void solve ()
{
    n = read (), k = read (), b = read (), s = read ();
    int r = s / k, l = 0, sum = 0;
    for (int i = 1; i < n; i++) sum += k - 1;
    l = (s - sum) / k;
    if (b < l || b > r) { printf ("-1\n"); return ; }
    printf ("%lld ", min (s, k * (b + 1) - 1));
    s -= min (s, k * (b + 1) - 1);
    for (int i = 2; i <= n; i++)
    {
        if (s >= k - 1) { printf ("%lld ", k - 1); s -= (k - 1); }
        else { printf ("%lld ", s); s -= s; }
    }
    printf ("\n");
}
signed main()
{
    int t = read ();
    while (t--) solve ();
    return 0;
}

Monoblock

解析:

题目大意

我们定义连续连续极长且都是一个相同的数的子段被称为一个“块”。一个序列的连续极长的块的个数表示为 \(g(1,n)\)。

给定一个长度 \(n\) 的序列和 \(m\) 次询问。对于每次询问,有两个数 \(i,x\),先将 \(a_i\) 改为 \(x\),然后输出 \(\sum\limits_{l=1}^n\sum\limits_{r=l}^ng(l,r)\),其中 \(g(l,r)\) 表示 \(a_{l\sim r}\) 的“块”数。


思路:

诈骗题,第一眼发现什么数据结构都不能很好维护,就去看D了,切了D回来也秒了,事实证明这是个错误的决定(如果先写D排名能提好几百)。

首先观察到可以数组直接统计维护。

首先注意到一个序列的块的个数实际上等价于元素个数-有多少个数与前一个数的值相同,前者又等价于序列长度,可以直接维护,后者相当于考虑先找出来整个序列这样的数出现的位置,设其中一个数 \(a_i\) 和 \(a_{i-1}\) 值相同,那么包括这两个元素的子序列都会有一个 \(-1\) 的贡献,这样的子序列要求左端点 \(l\) 在 \(l\in[1,i-1]\),右端点 \(r\) 在 \(r\in[i,n]\) 之间,这样的子序列的个数有 \((i-1)\times(n-i+1)\) 个。

原问题转化为 \(\sum\text{所有子序列长度}-\sum{\text{所有贡献}}\),可以分开维护,前者直接 \(\mathcal O(1)\) 算,后者开个数组维护每个位置对答案的贡献是 \(0\) 还是 \(-(i-1)\times (n-i+1)\),单点改就直接改当前位置的数组值就行。

然后随便记个 \(sum\) 就解决了。


code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
typedef pair <int, int> pii;
inline int read ()
{
    int x = 0, f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
    return x * f;
}
int n, q;
int a[N];
int c[N], sum, sum2;
void solve (int x) { if (a[x] == a[x - 1]) c[x] = (x - 1) * (n - x + 1); else c[x] = 0; }
signed main()
{
    n = read (), q = read ();
    for (int i = 1; i <= n; i++) a[i] = read ();
    for (int i = 2; i <= n; i++) solve (i);
    for (int i = 1; i <= n; i++) sum += c[i], sum2 += (1 + i) * i / 2;
    while (q--)
    {
        int x = read (), y = read ();
        a[x] = y;
        sum -= c[x]; sum -= c[x + 1];
        solve (x), solve (x + 1);
        sum += c[x]; sum += c[x + 1];
        printf ("%lld\n", sum2 - sum);
    }
    return 0;
}

2+ doors

解析:

题目大意:

Narrator有一个长度为 \(n\) 的整数数组 \(a\) ,但是他只会告诉你 \(n\) 的大小和 \(q\) 个要求,每个要求包括三个整数 \(i,j,x\) ,要求满足 \(a_i\mid a_j = x\),其中 \(|\) 表示按位或运算

找到满足所有要求的字典序最小的数组 \(a\)。数据保证有解。


思路:

大套路题,首先套路的发现每一个二进制位互补影响,可以分开考虑,原问题变成给你 \(q\) 个限制,每个限制要求 \(a_i|a_j\in{0,1}\)。

考虑字典序最小,我们需要让尽量前面的数分配 \(0\) 这样一定最优,我们考虑跟 \(a_1\) 有关的 \(q_1\) 个限制,如果 \(\exist i,j|(a_1|a_i=1,a_1|a_j=1\and a_i|a_j=0)\),那么必有 \(a_i,a_j\) 为 \(0\),\(a_1\) 为 \(1\),否则让 \(a_1=0\),\(\forall i|(a_1|a_i=0)\),令 \(a_i=1\) 一定最优。

那么考虑先把当前二进制位满足 \(a_i|a_j=0\) 的 \(i,j\) 强制为 \(0\),然后从 for 1 -> n 检索每一个 \(i\),如果 \(\exist j,j\in[i,n]\and a_j=0\),那么 \(a_i=1\),否则让有条件限制的都取1即可。

时间复杂度 \(\mathcal O(V(n+q))\),其中 \(V\) 取到 \(\log x\)。


code:

#include <bits/stdc++.h>
#define int long long
#define eb emplace_back
#define fi first
#define se second
#define mk make_pair
using namespace std;
const int N = 2e5 + 10;
const int INF = LLONG_MAX;
typedef pair <int, int> pii;
inline int read ()
{
    int x = 0, f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
    return x * f;
}
int n, m;
vector <pii> vec[N];
struct node {
    int x, y, k;
} s[N];
int a[32][N];
signed main()
{
    n = read (), m = read (); int mn = INF, mx = 0;
    for (int i = 1; i <= m; i++)
    {
        s[i].x = read (), s[i].y = read (), s[i].k = read ();
        if (s[i].x > s[i].y) swap (s[i].x, s[i].y);
        vec[s[i].x].eb (mk(s[i].y, s[i].k));
        mx = max (mx, s[i].k);
    }
    if (mx == 0) { while (n--) printf ("0 "); return 0; } // 场上最终挂掉的特判,挂因是0之间忘加空格了
    memset (a, -1, sizeof (a));
    for (int i = 31; i >= 0; i--)
    {
        for (int j = 1; j <= m; j++)
        {
            if (!((s[j].k >> i) & 1)) a[i][s[j].x] = 0, a[i][s[j].y] = 0;
            if (s[j].x == s[j].y) a[i][s[j].x] = (s[j].k >> i) & 1;
        }
        for (int j = 1; j <= n; j++)
        {
            if (a[i][j] != -1)
            {
                if (a[i][j] == 0)
                    for (int k = 0; k < vec[j].size (); k++)
                        if (((vec[j][k].se >> i) & 1) == 1) a[i][vec[j][k].fi] = 1;
                continue;
            }
            for (int k = 0; k < vec[j].size (); k++)
                if (a[i][vec[j][k].fi] == 0 && ((vec[j][k].se >> i) & 1) == 1) { a[i][j] = 1; break; }
            if (a[i][j] == 1) continue;
            a[i][j] = 0;
            for (int k = 0; k < vec[j].size (); k++)
                if (((vec[j][k].se >> i) & 1) == 1) a[i][vec[j][k].fi] = 1;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        int sum = 0;
        for (int j = 31; j >= 0; j--) sum += (a[j][i] << j);
        printf ("%lld ", sum);
    }
    return 0;
}

Long Way Home

解析:

题目大意:

有 \(n\) 座城市,城市间有 \(m\) 条双向道路,通过第 \(i\) 条道路需要花费 \(w_i\) 的时间,任意两个城市之间都有航班,乘坐城市 \(u\) 和 \(v\) 之间的航班需要花费 \((u-v)^2\) 的时间,但最多乘坐 \(k\) 次航班。
现在请对于任意城市 \(i(1 \le i \le n)\),求出从城市 \(1\) 出发,到达城市 \(i\) 所需要的最短时间。


思路:

大力 dp+dijkstra,场上想到了怎么设 dp 状态,但碍于时间没想出来,或许也是板子不熟吧,按理讲应该能一眼切的。

发现 \((u-v)^2\) 是斜率优化的板子,而 \(k\) 实际上就是一个常数,这告诉我们只需要做 \(k\) 次 \(dp\)。

我们考虑设 \(dp_{i,j}\) 表示从 \(1\) 到 \(i\) 飞了 \(j\) 次的最小距离,那么有:

\[dp_{i,j}=\min_{k=1}^n dp_{k,j-1}+(k-i)^2 \]

把这个东西化简一下:

\[dp_{i,j}=\min_{k=1}^n dp_{k,j-1}+k^2-2ik+i^2\\ dp_{i,j}-i^2=\min_{k=1}^n dp_{k,j-1}+k^2-2ik \]

那就是个斜率优化板子,我们考虑设 \(-2k\) 为 \(k\),\(k^2+dp_{k,j-1}\) 为 \(b\),那么维护一下下凸壳即可。

每次乘坐完飞机都跑一个dijkstra更新一下即可。


code:

#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define mk make_pair
using namespace std;
const int N = 1e5 + 10;
const int K = 20 + 10;
const int INF = 0x3f3f3f3f;
typedef pair <int, int> pii;
inline int read ( )
{
    int x = 0, f = 1;
    char ch = getchar ( );
    while (ch < '0' || ch > '9') {if (ch == '-') f = - 1; ch = getchar ( );}
    while (ch >= '0' && ch <='9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar ( );}
    return x * f;
}
int n, m, k;
struct edge {
	int ver, nxt, val;
} e[N << 1];
int head[N], tot;
void add_edge (int u, int v, int w) { e[++tot] = (edge) {v, head[u], w}; head[u] = tot; }
int dp[K][N];
bool vis[N];
int h, t;
struct node {
	int k, b, i;
	double x, y;
} l[N];
inline double calc(node x, node y) { return double(y.b - x.b) / double(x.k - y.k); }
void dijkstra (int s, int x)
{
	priority_queue <pii> que;
	memset (vis, 0, sizeof (vis));
	for (int i = 1; i <= n; i++) if (dp[x][i] != INF) que.push (mk (-dp[x][i], i));
	while (!que.empty ())
	{
		int u = que.top ().second; que.pop ();
		if(vis[u]) continue;
		vis[u] = true;
		for (int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].ver;
			if (dp[x][v] > dp[x][u] + e[i].val)
			{
				dp[x][v] = dp[x][u] + e[i].val;
				que.push (mk (-dp[x][v], v));
			}
		}
	}
}
signed main()
{
	n = read (), m = read (), k = read ();
	for (int i = 1; i <= m; i++)
	{
		int u = read (), v = read (), w = read ();
		add_edge (u, v, w);
		add_edge (v, u, w);
	}
	memset (dp[0], 0x3f, sizeof (dp[0]));
	dp[0][1] = 0;
	for (int s = 0; s <= k; s++)
	{
		if (s)
		{
			h = 1; t = 0;
			for (int i = 1; i <= n; i++)
			{
                if (dp[s - 1][i] == INF) continue;
				node now = (node) { -2 * i, i * i + dp[s - 1][i], i, -INF, INF};
				while (h <= t && calc (l[t], now) <= l[t].x) t--;
				if (h <= t) l[t].y = now.x = calc (l[t], now);
				l[++t] = now;
			}
			for (int i = 2; i <= n; i++)
			{
				while (h <= t && l[h].y < i) h++;
				int k = l[h].i;
				dp[s][i] = dp[s - 1][k] + (k - i) * (k - i);
			}
			for (int i = 1; i <= n; i++) dp[s][i] = min (dp[s - 1][i], dp[s][i]);
		}
		dijkstra (1, s);
	}
	for (int i = 1; i <= n; i++) printf ("%lld ", dp[k][i]);
    return 0;
}

6

解析:

题目大意:


思路:

交互&人类智慧题,不会,咕了


code:


标签:ch,int,Codeforces,read,while,Div,CodeForces1715,dp,define
From: https://www.cnblogs.com/violin-wyl/p/16632301.html

相关文章

  • CF1721D(Edu134Div2-D)
    原题链接一个显然的结论是,从高位道低位考虑答案在这一位是否可以是1,那么如果一个高位可以为1,那么一定不会为了其他低位而把它变成0。另一个结论是:如果一个高位不能变成1,那......
  • 学习随笔——codeforces题目Build Permutation解答
    摘要:本题属于构造算法,虽然简单但对思维提升有一定帮助题目原地址如下:https://codeforces.com/problemset/problem/1713/C题目截图如下:  关键词:构造算法,动态规划,*120......
  • 【Virt.Contest】CF1321(div.2)
    第一次打虚拟赛。CF传送门T1:ContestforRobots统计\(r[i]=1\)且\(b[i]=0\)的位数\(t1\)和\(r[i]=0\)且\(b[i]=1\)的位数\(t2\)。两个数都为\(0\)或都为......
  • 【Virt.Contest】CF1215(div.2)
    第二次打虚拟赛。CF传送门T1:YellowCards黄色卡片中规中矩的\(T1\)。首先可以算出一个人也不罚下时发出的最多黄牌数:\(sum=a1*(k1-1)+a2*(k2-1)\)此时,若\(sum>=n......
  • Codeforces Round #813 (Div. 2) A - E2
    A:一组长度为n的排列,问交换多少次,能让前m个数变成[1,m]中的数输出前m个数中有多少个比m大的就可以了//-------------------------代码----------------------------......
  • codeforces round #815 (div.2) B. Interesting Sum
    一开始的想法是n^2时间暴力枚举片段的开头和结尾,但是时间肯定不行。所以干脆想办法缩减时间,用个priority_queue呀,甚至尝试着动态规划。但是很显然无论如何这种东西没法dp,完......
  • Codeforces Round #814 (Div. 2) A-D2
    A:ChipGame题意:只能向上走和向右走,走到右上角,最后一步谁操作谁就赢只要判断总步数的奇偶性就可以了//-------------------------代码----------------------------......
  • Codeforces Round #779 D
    D1我们先来看D1啊我最开始理解的就是一个翻转但是只在0开始时才是正确的这里就有一组hack1203他会输出0而不是3为啥???这样一想好像是正确的每次要是01数字不同......
  • Educational Codeforces Round 106 (Rated for Div. 2) | CF1499
    E一个暴力是显然的,\(f(i,j,k)\)表示当前已经使用\(a\)的前\(i\)位,\(b\)的前\(j\)位,最后一位是\(a\)还是\(b\)的。然后\(O(n^2)\)枚举起点跑下去即可。为啥......
  • [Oracle] LeetCode 1010 Pairs of Songs With Total Durations Divisible by 60
    Youaregivenalistofsongswheretheithsonghasadurationoftime[i]seconds.Returnthenumberofpairsofsongsforwhichtheirtotaldurationinsecon......