CSP模拟10
Because love you everyday.
A. Because
签到题,但是要特判,挂 \(\text{40 pts}\)。
B. Love
双指针。
思路感觉很清奇。
我们用结构体存每一个数,保存它的值和它所属的集合,然后按照值把所有集合的数放到一起去。
然后用双指针维护。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <climits>
using namespace std;
const int N = 1005000;
int n,k,c[N];
int cnt;
struct Node{
int val,id;
}a[N << 2];
bool cmp(Node x,Node y) {
return x.val < y.val;
}
int vis[N],tot;
int main() {
ios::sync_with_stdio(false);
cin >> k;
for(int i = 1;i <= k; i++) {
cin >> c[i];
for(int j = 1;j <= c[i]; j++) {
cnt ++;
cin >> a[cnt].val;
a[cnt].id = i;
}
}
n = cnt;
sort(a + 1,a + n + 1,cmp);
int l,r,ans = INT_MAX;
for(l = 1,r = 1;r <= n; r ++) {
if(vis[a[r].id])
vis[a[r].id] ++;
else {
vis[a[r].id] ++;
tot ++;
}
while(tot == k) {
if(tot == k)
ans = min(ans,a[r].val - a[l].val);
if(vis[a[l].id] == 1)
tot --;
vis[a[l].id] --;
l ++;
}
}
cout << ans;
return 0;
}
C. You
咕了。
D. Everyday
我们令一个区间的宝石能组成的价值范围为一个集合。
我们先讨论宝石如何组成不同的价值。
先考虑每个宝石必须被选的情况,那么对于两个宝石,他们的价值区间是 \(\left[l_1,r_1 \right]\) 和 \(\left[l_2,r_2 \right]\),他们两个能表示的区间即为二者左右区间各自相加,即 \(\left[l_1 + l_2,r_1 + r_2 \right]\)。
然后再考虑不选某个宝石的情况,我们给每个宝石能够组成的价值范围加上一个 \(\left[ 0,0 \right]\),这样表示不选该宝石的情况,容易发现,这样仍然符合原本的运算方式,左右边界相加。
所以我们可以用线段树维护,每个结点表示的是对应区间的宝石能够组成的价值区间的集合。
Pushup
时直接进行 \(\mathcal{O}(n^2)\) 的暴力合并,注意处理一下有没有重叠的区间,把他们合并。
这样我们就可以利用线段树进行区间查询了。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define N 100500
#define int long long
typedef pair<int,int> Pair;
typedef vector<Pair> Vector;
int n,m;
Pair a[N];
struct Segment{
int l,r;
Vector Set;
}t[N << 2];
#define lid id << 1
#define rid id << 1 | 1
class Segment_Tree{
private:
Vector Merge(Vector l,Vector r) {
if(l.empty())
return r;
if(r.empty())
return l;
Vector res;
for(int i = 0;i < l.size(); i++)
for(int j = 0;j < r.size(); j++)
res.push_back(make_pair(l[i].first + r[j].first,l[i].second + r[j].second));
stable_sort(res.begin(),res.end());
int len = res.size();
for(auto iter = res.begin();iter != res.end();) {
auto iter2 = next(iter);
if(iter2 == res.end())
break;
if (iter2->first <= iter->second) {
iter->second = max(iter->second, iter2->second);
res.erase(iter2);
}
else {
++iter;
}
}
return res;
}
void Pushup(int id) {
t[id].Set = Merge(t[lid].Set,t[rid].Set);
return ;
}
public:
void Build(int id,int l,int r) {
t[id].l = l;
t[id].r = r;
if(l == r) {
t[id].Set.push_back(make_pair(0,0));
t[id].Set.push_back(a[l]);
return ;
}
int mid = (l + r) >> 1;
Build(lid,l,mid);
Build(rid,mid + 1,r);
Pushup(id);
return ;
}
void Change(int id,int pos,Vector val) {
if(t[id].l == t[id].r) {
t[id].Set = val;
return ;
}
int mid = (t[id].l + t[id].r) >> 1;
if(pos <= mid)
Change(lid,pos,val);
else
Change(rid,pos,val);
Pushup(id);
return ;
}
Vector Query(int id,int l,int r) {
if(l <= t[id].l && t[id].r <= r)
return t[id].Set;
int mid = (t[id].l + t[id].r) >> 1;
Vector tmp1,tmp2,ans;
if(l <= mid)
tmp1 = Query(lid,l,r);
if(r > mid)
tmp2 = Query(rid,l,r);
ans = Merge(tmp1,tmp2);
return ans;
}
}tree;
#undef lid
#undef rid
signed main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 1;i <= n; i++)
cin >> a[i].first >> a[i].second;
tree.Build(1,1,n);
for(int i = 1,opt,k,l,r;i <= m; i++) {
cin >> opt;
if(opt == 1) {
cin >> k >> l >> r;
Vector tmp;
tmp.push_back(make_pair(0,0));
tmp.push_back(make_pair(l,r));
tree.Change(1,k,tmp);
}
else {
cin >> l >> r;
Vector ans = tree.Query(1,l,r);
int sum = 0;
for(int i = 0;i < ans.size(); i++)
sum += ans[i].second - ans[i].first + 1;
cout << sum << "\n";
}
}
return 0;
}