24山东省赛vp补题
I Left Shifting
知识点:签到题,无
思路:先判断字符串一开始是否满足条件,若不满足从第一个开始遍历,若出现 \(s[i]==s[i+1]\),那么输出 \(i+1\) 即可,若遍历完也没发现,输出 \(-1\)
A Printer
知识点:二分
思路:对秒数进行二分,然后跑check检查,注意跑的时候如果发现 \(sum>=k\),一定要直接返回 \(1\),不然会爆数据范围。
F Divide the Sequence
知识点:贪心,前缀和
这个答案应该是求一个长式子的最大值,对其进行化简。
假设整个序列的总和为 $S=\displaystyle\sum_{j=1}^{n} a_{j \text { 。 }} $
设划分点为 $ r_{1}, r_{2}, \ldots, r_{k-1}$ ,前缀和为 $P_{x}=\displaystyle\sum_{j=1}^{x} a_{j} $ 。 那么:
代入目标公式:
展开并合并同类项:
被减数是固定的,那么只需要使得减数很小就行了,所以取最小的几个前缀和就可以了,对原数组进行前缀和,然后对前缀和进行排序,再进行前缀和,根据k的大小取值就可以了
Code
点击查看代码
void solve()
{
cin >> n;
vi a(n + 1);
vi pre(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
pre[i] = pre[i - 1] + a[i];
}
sort(pre.begin() + 1, pre.begin() + n);
vi b(n + 1);
for (int i = 1; i < n; i++)
{
b[i] = b[i - 1] + pre[i];
}
for (int i = 1; i <= n; i++)
{
if (i == 1)
{
cout << pre[n] << ' ';
continue;
}
cout << i * pre[n] - b[i - 1] << ' ';
}
cout << endl;
}
K Matrix
知识点:构造
把1,2,3,4放到四角,四个边上全是5,然后对内部进行构造。
不难发现,就算穿插着放一个相同数字,总会有情况不满足,所以 \(n\) 行只能放一行,列也是一样,这样一共可以放 \(5+n-2+n-2-1=n\) 个数字,恰好放完。
J Colorful Spanning Tree
知识点:最小生成树kruskal
思路:把边都存起来,按权值从小到大排序,遍历所有边,
如果两者根相同且根不连通,说明这两个点是同一颜色的,需要连 \(a[i]-1\) 个边,如果两者根相同且根连通,说明这两个点连过了,不需要再连
如果两者根不同且两者均不连通,说明两个点分属于两个块,需要从左块连 \(a[v]\) 条边,从右块往左块连 \(a[u]-1\) 条边
如果两者根不同且有一个连通,只需要将未连通的连到连通块上即可
Code
点击查看代码
int fa[M];
void init()
{
for (int i = 1; i <= n; i++)
{
fa[i] = i;
}
}
int find(int a)
{
return fa[a] == a ? a : fa[a] = find(fa[a]);
}
bool same(int a, int b)
{
return find(a) == find(b);
}
void join(int a, int b)
{
a = find(a);
b = find(b);
if (a != b)
{
fa[a] = b;
}
}
struct node
{
int u, v, w;
};
bool cmp(node a, node b)
{
return a.w < b.w;
}
void solve()
{
cin >> n;
vector<node> adj;
init();
vi a(n + 1);
vi vis(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
vvi b(n + 1, vi(n + 1));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> b[i][j];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
adj.push_back({i, j, b[i][j]});
}
}
sort(all(adj), cmp);
int ans = 0;
for (auto [u, v, w] : adj)
{
u = find(u);
v = find(v);
cerr << u << ' ' << v << ' ' << w << endl;
if (u == v)
{
if (!vis[u])
{
vis[u] = 1;
ans += (a[u] - 1) * w;
}
continue;
}
if (vis[u] && vis[v])
{
ans += w;
fa[u] = v;
continue;
}
if (vis[u])
{
ans += (a[v] * w);
fa[v] = u;
continue;
}
if (vis[v])
{
ans += (a[u] * w);
fa[u] = v;
continue;
}
ans += (a[v] + a[u] - 1) * w;
fa[u] = v;
vis[v] = 1;
}
cout << ans << endl;
}
D Hero of the Kingdom
知识点:数学
思路:从a地运到b地,肯定是买卖数相同才赚的最多,最省时间,所以 \(x=y\)。
同时要注意时间不会超时,如果发现还有时间,就可以再拿着赚到的钱再回去买,就相当于重复前面这个过程。
**注意:如果每次收益不够买到下一个物品,而且时间花费小,时间大的话,一定会超时,所以要进行优化,找出“一次性买x个商品这样的循环”还会持续多少次,记为k,应该有时间和金钱两个限制,金钱上,应该有 \(k*(q-p)*x>=p*(x+1)-m\) **,意思为“以我当前每次赚的钱还需要赚多少次才能满足买 \(x+1\) 个”;时间上,应该有 \(t>=k*((a + c) * x + b + d)\)
Code
点击查看代码
void _()
{
int q, p, a, b, c, d, m, t;
cin >> p >> a >> b >> q >> c >> d >> m >> t;
while (1)
{
if (t <= b + d || m < p)
break;
int x = (t - b - d) / (a + c);
if (x == 0)
break;
if (p * x > m)
{
x = m / p;
}
int tmp = min((p * (x + 1) - m + (q - p) * x - 1) / ((q - p) * x), t / ((a + c) * x + b + d) - 1);
if (tmp <= 0)
{
m += (q - p) * x;
t -= (a + c) * x + b + d;
}
else
{
m += (q - p) * x * tmp;
t -= ((a + c) * x + b + d) * tmp;
}
}
cout << m << endl;
}
C Colorful Segments 2
知识点:单调队列,(权值线段树)
思路:对于所有线段,按左端点的大小进行排序,那么对每一条边来说,只需要看当前多少条边的右端点比自己的左端点大就可以了,用优先队列小根堆维护一个单调队列,使得队列中所有元素都比当前线段的左端点大就可以了。
点击查看代码
struct node
{
int l, r;
};
bool cmp(node a, node b)
{
return a.l < b.l;
}
void solve()
{
cin >> n >> k;
vector<node> a(n);
for (int i = 0; i < n; i++)
{
cin >> a[i].l >> a[i].r;
}
sort(all(a), cmp);
priority_queue<int, vector<int>, greater<int>> p;
int ans = 1;
for (int i = 0; i < n; i++)
{
while (!p.empty() && p.top() < a[i].l)
{
p.pop();
}
int now = k - p.size();
if (now <= 0)
{
ans = 0;
break;
}
ans = ans * now % mod;
p.push(a[i].r);
}
cout << ans << endl;
}

浙公网安备 33010602011771号