LGP17706 ICPC 2019 Nanjing R Prince and Princess
原题链接:ICPC 2019 Nanjing R Prince and Princess
分析
当然了,如果说没有任何的反对者或者漠不关心者,那么,显然可以通过一次询问直接得出答案。
那么,如果说只存在反对者,结果同理,王子需要询问......好像没有这个数据范围......
难道说只有只存在支持者的时候才能行吗?这并不对......
说实在地,我觉得应该只进行第三个询问......
那么,会出现什么情况呢?
似乎只有成环的时候无法确定......
为啥 tag 是 greedy?
那么,不如进行雷霆大分讨!
那是啥啊😭
居然被我猜对了,但是,没有猜全......
确实,我们发现只进行第三个询问是非常优秀的,然后,我们最多使用 ( b + c ) × 2 + 1 (b+c)\times 2+1 (b+c)×2+1 次询问即可 。
相当于说把中立者当成反对者,在最劣的情况下,我们需要 b + c + 1 b+c+1 b+c+1 次才能发现谎言,剩下的全都是支持者,所以,需要 ( b + c ) × 2 + 1 (b+c)\times 2+1 (b+c)×2+1 次询问。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
int a, b, c;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> a >> b >> c;
if (a == 1 && b == 0 && c == 0){
cout << "YES" << '\n';
cout << 0;
return 0;
}
if (a > b + c){
cout << "YES" << '\n';
cout << (b + c) * 2 + 1;
return 0;
}
else{
cout << "NO";
}
}
LGP17784 山田凉不是罪过
原题链接:山田凉不是罪过
分析
思考一下,那么,对于单个测试点分数较高的,我们尽可能使得其包含的任务数量尽可能少......然后,还需要满足一共有 n n n 个测试点......思考一下......
别急,这个怎么那么像什么性价比贪心之类的东西......
题目转化为:构造一个数组 b b b,使得 ∑ i = 1 k b i = n \sum\limits_{i=1}^{k}b_i=n i=1∑kbi=n,且 ∑ i = 1 k a i 2 b i \sum\limits_{i=1}^{k}{\dfrac{a_i}{2^{b_i}}} i=1∑k2biai 取到最大值。呃,可能这个式子有点糙,是错的,但是,不要紧。
那岂不是将 a a a 进行降序排序,前面的都只给一个,最后一个给全?真的假的?
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
const int N = 1000005;
int n, k, a[N];
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
int mn = 0x3f3f3f3f, pos = 0;
for (int i = 1; i <= k; i++){
cin >> a[i];
if (a[i] < mn){
mn = a[i];
pos = i;
}
}
sort(a + 1, a + k + 1, greater<int>());
int ans = 0;
for (int i = 1; i < k; i++){
ans = (ans + a[i] * qpow(2, mod - 2)) % mod;
}
int tmp = qpow(2, (n - k + 1));
ans = (ans + a[k] * qpow(tmp, mod - 2)) % mod;
cout << ans << "\n";
for (int i = 1; i <= k; i++){
if (i == pos){
cout << n - k + 1 << " ";
}
else{
cout << 1 << " ";
}
}
}
LGP17678 IO 2025 #4 拆钞幻术
原题链接:IO 2025 #4 拆钞幻术
分析
有解和无解是容易判定的,就是我们对比一下和的大小即可,这个前缀和还挺好做的......
然后,我们要思考最小的特效费用......难不成从大到小直接每次整出来一点就行?居然被我猜对了......
那么,思考一下,这就需要我们快速地排序并统计了......然而,这个并不容易快速统计......呃......别搞啊,黄题这样吗😭
我会做 O ( q n l o g n ) O(qnlogn) O(qnlogn) 之类的东西,但是,题目明显让我做 O ( q l o g n ) O(qlogn) O(qlogn) 之类的东西......
不是,那我咋才能做到啊?不是,我统计至少也是 O ( q n ) O(qn) O(qn) 的啊?
我们发现,我们每次的答案是(先默认 a a a 在 l , r l,r l,r 上有序):
∑ i = l r ( S − ∑ j = l i − 1 a j ) × a i \sum\limits_{i=l}^{r}{(S-\sum\limits_{j=l}^{i-1}{a_j})\times a_i} i=l∑r(S−j=l∑i−1aj)×ai
后面的还好说,我怎么保证其有序呢?怎么快速求解呢?
难不成要离线?
什么叫离线+线段树?这是黄?
哦不不不,我们发现 ,在我们证明的时候,可以写出式子:
S 2 − ∑ a i 2 2 \dfrac{S^2-\sum\limits_{}^{}{a_i^2}}{2} 2S2−∑ai2
那么,我们要使得后面尽可能大,所以,倒叙排序是对的。那么,对于一段区间,直接减着做就行。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int n, q, x[N];
int sum1[N], sum2[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; i++){
cin >> x[i];
sum1[i] = sum1[i - 1] + x[i];
sum2[i] = sum2[i - 1] + x[i] * x[i];
}
for (int cs = 1, l, r, S; cs <= q; cs++){
cin >> l >> r >> S;
int tmp = sum1[r] - sum1[l - 1];
if (S < tmp)
cout << -1 << "\n";
else
cout << (2 * S * tmp - tmp * tmp - sum2[r] + sum2[l - 1]) / 2 << "\n";
}
}
LGP17677 IO 2025 #4 现在你看见我了
原题链接:IO 2025 #4 现在你看见我了
分析
我发现,我们似乎统计出现过最少的字符,然后,直接加 1 1 1 输出就行?
好久没有量化直觉了......思考一下我是怎么想到的......我感觉......
为啥不过啊???
哦,对于一段,只要他没有出现一些字符,则可以添加上来使得不是子串......为什么那么奇怪呢......
哦哦哦,就是说,一旦一个区间有未出现的,那么,不管他,但是,一旦有一个所有都出现的,那么,可以将其"缩起来"?然后,总长度加一下就行了......
所以,这个究竟在贪心什么?!?极大匹配!?
正解
cpp
#include <bits/stdc++.h>
using namespace std;
int n;
string s;
int cnt[26];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
cin >> s;
int ans = 0;
for (int i = 0; i < (int)s.length(); i++){
cnt[s[i] - 'a']++;
bool flag = true;
for (int j = 0; j < n; j++){
if (cnt[j] == 0){
flag = false;
break;
}
}
if (flag){
ans++;
memset(cnt, 0, sizeof(cnt));
}
}
cout << ans + 1;
}
LGP17584 【LABOI-R1】过客 / guest
原题链接:【LABOI-R1】过客 / guest
分析
呃......曼哈顿距离是个什么来着......我是说他的图形......哦,是一个以 ( x , y ) (x,y) (x,y) 为对角线交点的倾斜 π 4 \dfrac{\pi}{4} 4π 的的正方形......
首先,这个小 Z Z Z 必须到达这个正方形里面,然后,在这个正方形里往返移动即可。而且,每次他移动的距离都是 1 1 1,相对可靠一点吧。
那么,可以知道的是,对于 k = 0 k=0 k=0,我们一方面求出到达该点的距离,然后对于剩下的步数我们需要除以 2 2 2;剩下的,我们需要知道的是,我们至少走多少步可以到达正方形里面。
我们假设一个点 ( x + a , y + k − a ) (x+a,y+k-a) (x+a,y+k−a),求 ∣ s − x − a ∣ + ∣ t − y − k + a ∣ |s-x-a|+|t-y-k+a| ∣s−x−a∣+∣t−y−k+a∣ 的最小值......按照之前的经验,我们可以过 ( s , t ) (s,t) (s,t) 做一条与正方形某条边平行的线,发现,走到正方形一个顶点,当然,如果可以,直接走到边上似乎也不赖......总之分讨一下吧......
好吧,有点小问题......
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
int q;
int x, y, s, t, k, l;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> q;
for (int cs = 1; cs <= q; cs++){
cin >> x >> y >> s >> t >> k >> l;
int d = abs(x - s) + abs(y - t);
int ans = 0;
if (k == 0){
if (s == x && t == y){
ans = l / 2;
}
else{
if (l >= d){
ans = 1 + (l - d) / 2;
}
}
}
else{
if (d <= k){
ans = l;
}
else{
ans = l - (d - k) + 1;
}
}
cout << max(ans, 0ll) << "\n";
}
}
LGP17511 入门赛 #52 月饼调配
原题链接:入门赛 #52 月饼调配
分析
刚才使用 AI 把之前的哪个结构体给调过去了,呃,感觉对我的帮助并不大......场上要真的碰到了怎么办啊😭
贪心地写,多了就拿走,少了就往里面拿......呃......具体地,我们从叶子开始考虑,把多的给他的父亲,如果少了,就找父亲要,然后动态的加减,应该也不会很慢。每次尽可能地使用通道......这个实现的难度主要在代码吧......
啊?好像并不是特别困难?
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int n;
int s[N], t[N];
vector<pair<int, int>> e[N];
int val[N];
int ans = 0;
void dfs(int u, int fa){
for (auto tmp : e[u]){
if (tmp.first == fa)
continue;
dfs(tmp.first, u);
val[u] += val[tmp.first];
ans += (abs(val[tmp.first]) + tmp.second - 1) / tmp.second;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> s[i];
}
for (int i = 1; i <= n; i++){
cin >> t[i];
val[i] = s[i] - t[i];
}
for (int i = 1, u, v, c; i < n; i++){
cin >> u >> v >> c;
e[u].push_back({v, c});
e[v].push_back({u, c});
}
dfs(1, 0);
cout << ans;
}
LGP5690 CSP-S 2019 江西 日期
原题链接:CSP-S 2019 江西 日期
分析
爆搜居然写挂了😭
正解
cpp
#include <bits/stdc++.h>
using namespace std;
string s, t;
int ans = 6;
int a[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void dfs(int p, int res){
if (p == 5){
int mo = (s[0] - '0') * 10 + (s[1] - '0');
int dy = (s[3] - '0') * 10 + (s[4] - '0');
if (mo <= 0 || mo > 12)
return ;
if (dy <= 0 || dy > a[mo])
return ;
ans = min(ans, res);
return ;
}
if (p == 2){
dfs(p + 1, res);
}
else{
for (int i = 0; i <= 9; i++){
s[p] = (char)(i + '0');
if (s[p] != t[p])
dfs(p + 1, res + 1);
else
dfs(p + 1, res);
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
s = t;
dfs(0, 0);
cout << ans;
}
LGP14402 JOISC 2016 危险的滑冰 / Dangerous Skating
原题链接:JOISC 2016 危险的滑冰 / Dangerous Skating
分析
这个给出了 O ( n 2 ) O(n^2) O(n2) 甚至 O ( n 2 l o g n ) O(n^2logn) O(n2logn) 的可能......
难道说我可以建图吗?但是,怎么控制每次新加入一块冰呢?
怎么建图呢?我觉得我们需要用一些贪心的思想了......
首先,我们肯定要清理一些"杂兵",我们把那些不连通的、位置重合的直接特判掉。
然后,思考建图......
可以这样建图吗?就是说,对于一个位置,其四周最近的冰块,我们将两者距离设为边权即可连边......然后跑最短路......似乎就做完了?那么,我们还是没有把启点变成冰块考虑进去,但是,我们发现,每次反弹,都需要 2 2 2 的代价,这个真实太妙了!就是说,我们过去一趟,回来一趟,恰好是"反弹",然后,似乎是做完了?我不知道......可能吧......好吧......
看一下具体怎么实现......
话说回来,我去年为什么不写呢?这个去年不会是个紫吧......哦......去年我是不是想把主页的颜色调成紫色所以不写蓝题?我咋这么幼稚呢?
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1005;
int r, c;
int r1, c1;
int r2, c2;
char ch[N][N];
int L[N * N], R[N * N], T[N * N], D[N * N];
int dis[N * N];
vector<pair<int, int>> e[N * N];
void dijkstra(int S){
memset(dis, 0x3f, sizeof(dis));
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
dis[S] = 0;
q.push({0, S});
while (!q.empty()){
auto tmp1 = q.top();
q.pop();
for (auto tmp2 : e[tmp1.second]){
if (dis[tmp2.first] > dis[tmp1.second] + tmp2.second){
dis[tmp2.first] = dis[tmp1.second] + tmp2.second;
q.push({dis[tmp2.first], tmp2.first});
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> r >> c;
for (int i = 1; i <= r; i++){
for (int j = 1; j <= c; j++){
cin >> ch[i][j];
}
}
cin >> r1 >> c1;
cin >> r2 >> c2;
int lst = 0;
for (int i = 1; i <= r; i++){
for (int j = 1; j <= c; j++){
if (ch[i][j] == '#'){
lst = j;
}
else{
L[(i - 1) * c + j] = (i - 1) * c + lst + 1;
}
}
}
lst = 0;
for (int i = 1; i <= r; i++){
for (int j = c; j >= 1; j--){
if (ch[i][j] == '#'){
lst = j;
}
else{
R[(i - 1) * c + j] = (i - 1) * c + lst - 1;
}
}
}
lst = 0;
for (int i = 1; i <= c; i++){
for (int j = 1; j <= r; j++){
if (ch[j][i] == '#'){
lst = j;
}
else{
T[(j - 1) * c + i] = lst * c + i;
}
}
}
lst = 0;
for (int i = 1; i <= c; i++){
for (int j = r; j >= 1; j--){
if (ch[j][i] == '#'){
lst = j;
}
else{
D[(j - 1) * c + i] = (lst - 2) * c + i;
}
}
}
for (int i = 1; i <= r; i++){
for (int j = 1; j <= c; j++){
int pos = (i - 1) * c + j;
if (ch[i][j] == '#')
continue;
if (ch[i - 1][j] == '.')
e[pos].push_back({(i - 1 - 1) * c + j, 2});
if (ch[i + 1][j] == '.')
e[pos].push_back({(i - 1 + 1) * c + j, 2});
if (ch[i][j - 1] == '.')
e[pos].push_back({(i - 1) * c + j - 1, 2});
if (ch[i][j + 1] == '.')
e[pos].push_back({(i - 1) * c + j + 1, 2});
e[pos].push_back({L[pos], 1});
e[pos].push_back({R[pos], 1});
e[pos].push_back({T[pos], 1});
e[pos].push_back({D[pos], 1});
}
}
dijkstra((r1 - 1) * c + c1);
if (dis[(r2 - 1) * c + c2] == 0x3f3f3f3f3f3f3f3f)
cout << -1;
else
cout << dis[(r2 - 1) * c + c2];
}