2026-04-12~13 hetao1733837 的刷题记录
04-12
LGP4799 [CEOI 2015] 世界冰球锦标赛 (Day2)
原题链接:[CEOI 2015] 世界冰球锦标赛 (Day2)
分析
不是等一下,这种和的东西......背包?那不炸我吃!这个太大了......思考类比 NOIP2025T1 ,发现好像并不一样......
观察到 N N N 小的可怜,所以,考虑爆搜......好像真的可以!我宕机一下......那似乎还要搞一点组合数......又复杂了......就是把相同的搓成一堆......思考一下......
好的,场上的话,不会 折半搜索 可以打一个爆搜,打一个背包,这样理论上获得了 70pts 的好成绩。
考虑正解------折半搜索。
这么聪明!我们爆搜前一部分和后一部分,搞出来所有可能的和,然后,把其中一个排序,另外一个直接遍历,然后,与 m m m 做差,在第一个里面二分,即可统计答案!
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 50;
int n, mid;
long long m, inp[N];
vector<long long> suma, sumb;
void dfsa(int L, int R, long long sum){
if (sum > m)
return ;
if (L > R){
suma.push_back(sum);
return ;
}
dfsa(L + 1, R, sum + inp[L]);
dfsa(L + 1, R, sum);
}
void dfsb(int L, int R, long long sum){
if (sum > m)
return ;
if (L > R){
sumb.push_back(sum);
return ;
}
dfsb(L + 1, R, sum + inp[L]);
dfsb(L + 1, R, sum);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
mid = n / 2;
for (int i = 1; i <= n; i++){
cin >> inp[i];
}
dfsa(1, mid, 0);
dfsb(mid + 1, n, 0);
sort(suma.begin(), suma.end());
long long ans = 0;
for (auto v : sumb){
ans += upper_bound(suma.begin(), suma.end(), m - v) - suma.begin();
}
cout << ans;
}
04-13
LGP2119 [NOIP 2016 普及组] 魔法阵
原题链接:[NOIP 2016 普及组] 魔法阵
分析
显然有一个 O ( m 4 ) O(m^4) O(m4) 左右的暴力。感觉可以套一坨二分......但是,显然比较复杂。思考一下正解🤔
但是,tag 里给了 mathematics,就很微妙了。
思考一下......
拆一下......
要求 X a < X b < X c < X d X_a<X_b<X_c<X_d Xa<Xb<Xc<Xd,且 X b − X a = 2 X d − 2 X c X_b-X_a=2X_d-2X_c Xb−Xa=2Xd−2Xc,且 X b − X a < X c 3 − X b 3 X_b-X_a<\frac{X_c}{3}-\frac{X_b}{3} Xb−Xa<3Xc−3Xb,即 4 X b 3 < X c + 3 X a 3 \frac{4X_b}{3}<\frac{X_c+3X_a}{3} 34Xb<3Xc+3Xa,即 4 X b < X c + 3 X a 4X_b<X_c+3X_a 4Xb<Xc+3Xa。
总结为
X a < X b < X c < X d , X b − X a = 2 X d − 2 X c , 4 X b < X c + 3 X a X_a<X_b<X_c<X_d,X_b-X_a=2X_d-2X_c,4X_b<X_c+3X_a Xa<Xb<Xc<Xd,Xb−Xa=2Xd−2Xc,4Xb<Xc+3Xa
我不会/ll
我要看题解/ll
O ( m 4 ) O(m^4) O(m4) 居然有 50 p t s 50pts 50pts😱
然后排个序就 60 p t s 60pts 60pts 了😱
发现,设一些未知数,得出
X a + 2 t = X b , X b + 6 t + k = X c , X c + t = X d X_a+2t=X_b,X_b+6t+k=X_c,X_c+t=X_d Xa+2t=Xb,Xb+6t+k=Xc,Xc+t=Xd
设 k = 1 k=1 k=1,限制 X d X_d Xd 即可确定剩余几个,即可统计答案。 O ( n 2 ) O(n^2) O(n2) 能过?我**************************
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 15005, M = 40005;
int n, m, X[M];
int ans[M][5];
int buc[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
cin >> X[i];
buc[X[i]]++;
}
for (int t = 1; t * 9 <= n - 2; t++){
for (int xd = 9 * t + 2, tot = 0; xd <= n; xd++){
int xa = xd - 9 * t - 1;
int xb = xa + 2 * t;
int xc = xd - t;
tot += buc[xa] * buc[xb];
ans[xc][3] += tot * buc[xd];
ans[xd][4] += tot * buc[xc];
}
for (int xa = n - 9 * t -1, tot = 0; xa >= 1; xa--){
int xb = xa + 2 * t;
int xc = xb + 6 * t + 1;
int xd = xc + t;
tot += buc[xc] * buc[xd];
ans[xa][1] += tot * buc[xb];
ans[xb][2] += tot * buc[xa];
}
}
for (int i = 1; i <= m; i++){
cout << ans[X[i]][1] << " " << ans[X[i]][2] << " " << ans[X[i]][3] << " " << ans[X[i]][4] << '\n';
}
}
04-14
LGP7703 「MCOI-09」Dream and Strings REMATCH
原题链接:「MCOI-09」Dream and Strings REMATCH
分析
见到了算是缘分吧......
感觉会有一定的启发价值。
竟没有 tag \operatorname{tag} tag!
好的,那么一个 O ( 3 n ) O(3^n) O(3n) 的爆搜呼之欲出!发现 5000 ≤ N ≤ 10 5 T 5000 \le N \le \frac{10^5}{T} 5000≤N≤T105,我已急哭/ll
a i ≤ 10 18 a_i\le 10^{18} ai≤1018,背包也无法实现/ll
难道搞一个动态匹配的状物?一边为 1 1 1,则......
我干嘛要这样呢?我可以把一坨东西整成 0 0 0,当序列有重复时,就可以很爽了。问题在于不是怎么办......
显然,这题的复杂度要求不超过 O ( ∑ N l o g ∑ N ) O(\sum{N}log\sum{N}) O(∑Nlog∑N),那 ......难道我蒙一个基准值,然后硬凑?那好像至少 O ( n 2 ) O(n^2) O(n2) 啊......
其实,数据随机,我们可以直接交足够多的次数使得题目通过......
怎么办/ll
根据我巧妙地计算, O ( T ( ∑ n ) 2 ) O(T({\sum{n}})^2) O(T(∑n)2) 似乎可以过......因为我们可以进行一些巧妙地推到。
好吧,这个想法并不成熟。折半搜索用不了......
严肃决定打开题解......俺妈一会给我送东西(●'◡'●)
呃,升序排序后相邻两个配对相减,然后就做完了。
对,很简洁,复杂度证明基于其期望和一坨啊吧啊吧,得出复杂度来到了 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)。
实现上,对于每个值,维护两个集合,一个表示加这个值,另一个表示减这个位置的值,就是另外一些位置吧,如果一旦发现两个值相等,那么直接结束操作,然后把两个位置随即搞一搞,即可得出答案。
若不想等,假设 a < b a<b a<b,则要 b − a b-a b−a,那么,减 b b b 的集合和加 a a a 的集合合并,另外的两个集合同理。
严肃观看题解代码。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int T;
int n;
struct node{
int val;
set<int> add, rec;
bool operator<(const node k) const{
return val < k.val;
}
}a[N];
int ans[N];
node merge(node x, node y){
for (auto v : y.add){
x.rec.insert(v);
}
for (auto v : y.rec){
x.add.insert(v);
}
x.val -= y.val;
return x;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> T;
for (int cs = 1; cs <= T; cs++){
cin >> n;
int tmp = n;
for (int i = 1; i <= n; i++){
cin >> a[i].val;
a[i].add.clear();
a[i].rec.clear();
a[i].add.insert(i);
ans[i] = 0;
}
bool flag = false;
while (n){
int m = n >> 1;
sort(a + 1, a + n + 1);
for (int i = 1; i <= m; i++){
if (a[i * 2 - 1].val == a[i * 2].val){
flag = true;
for (auto v : a[i * 2 - 1].add)
ans[v] = 1;
for (auto v : a[i * 2 - 1].rec)
ans[v] = -1;
for (auto v : a[i * 2].add)
ans[v] = -1;
for (auto v : a[i * 2].rec)
ans[v] = 1;
break;
}
}
if (flag)
break;
for (int i = 1; i <= m; i++){
a[i] = merge(a[i * 2], a[i * 2 - 1]);
}
n >>= 1;
}
if (!flag){
cout << -1;
}
else{
for (int i = 1; i <= tmp; i++){
cout << ans[i] << " ";
}
}
cout << '\n';
}
}
最开始 merge 的位置写错了,所以全都 WA 了。