[NOI2019]序列
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2019]序列相关的知识,希望对你有一定的参考价值。
序列
题解
非要我评价的话我只能说*b题就这样的,网络流就网络流嘛,还非得手动模拟,就是屑。
首先,我们应该比较容易地构建出它的网络流模型。
至少有
L
L
L个选择的下标相同显然可以转化成最多选择
K
−
L
K-L
K−L对下标不同。
我们的网络流相当于构建一个二分图,图上所有
a
i
a_i
ai都可以随便和它对应的
b
i
b_i
bi相匹配,而不同的匹配最多只有
K
−
L
K-L
K−L个,连出来是长这样的:
我们要去求的是这个图的最大费用最大流图画得貌似有点丑,显然直接把边权倒过来跑肯定是跑不过
n
⩽
2
×
1
0
5
n\\leqslant 2\\times 10^5
n⩽2×105的。
对于这种简单分层图的费用流一种比较经典的方法是利用贪心来模拟网络流。
显然,这种退流也是比较容易整到可撤销贪心上的。
我们先观察这张图,显然,那
K
−
L
K-L
K−L条边的选择余地是最大的,所以说如果我们还没选满的话就可以直接把
A
A
A现在剩下的最大的一个和
B
B
B现在剩下的最大的一个给拿进去。
否则的话我们就有两种选择,一是将一对没有被选择的
(
a
i
,
b
i
)
(a_i,b_i)
(ai,bi)选上,一是撤销原先选上的一堆
(
a
i
,
b
j
)
(a_i,b_j)
(ai,bj),变成
(
a
i
,
b
i
)
+
(
a
x
,
b
y
)
(a_i,b_i)+(a_x,b_y)
(ai,bi)+(ax,by),这样都不会影响我们拿
K
−
L
K-L
K−L条边的选择。
我们可以直接把两种的改变量记录下来,比较哪种改变量更大就选哪种。
当然,直接维护这些可能不太好搞。
我们可以维护
a
a
a选在那
K
−
L
K-L
K−L条边中的集合,
b
b
b选在那
K
−
L
K-L
K−L条边中的集合,
a
a
a没有被选的集合,
b
b
b没有被选的集合。
显然,一种改变量就是后面两个集合的交集中和最大的那一堆,一种改变量就是把第一个集合中
i
i
i中最大的
b
i
b_i
bi加上,减去第二个集合中最小的
b
i
b_i
bi,在把后面两个集合中最大的拿出来。
这些都可以直接用优先队列维护。
大体思路就是这样的,不过还得注意一些细节。
比如说我们维护的加入那
K
−
L
K-L
K−L条边的集合是不能有交集的,否则它们就不会贡献在这里了。
也就是说我们后面加进来的点与它有交集的话,可以直接把它们都删掉。
这当然会导致后面又可以随便选边了,当然要记录一下。
另外就是它可能上面
(
a
i
,
b
j
)
−
>
(
a
i
,
b
i
)
+
(
a
x
,
b
y
)
(a_i,b_j)->(a_i,b_i)+(a_x,b_y)
(ai,bj)−>(ai,bi)+(ax,by)中
y
y
y也是可能为
j
j
j的,要特判一下。
当然,也可以是
(
a
i
,
b
j
)
−
>
(
a
j
,
b
j
)
+
(
a
x
,
b
y
)
(a_i,b_j)->(a_j,b_j)+(a_x,b_y)
(ai,bj)−>(aj,bj)+(ax,by)的,对称的嘛。
所以只需要维护堆就行了,每次都会多选一对点,最多只会选
K
K
K次。
总时间复杂度
O
(
∑
n
log
n
)
O\\left(\\sum n\\log n\\right)
O(∑nlogn)。
源码
维护
7
7
7个堆也可以过。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL,int> pii;
#define MAXN 200005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=998244353;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=15;
const LL INF=0x3f3f3f3f3f3f3f3f;
const double Pi=acos(-1.0);
const double eps=1e-9;
const int lim=1000000;
const int orG=3,ivG=332748118;
const int n1=500;
const int M=MAXN/n1+5,N=n1+5;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
x*=f;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int T,n,K,L,a[MAXN],b[MAXN];LL ans;
bool visa[MAXN],visb[MAXN];
priority_queue<pii>A1,B1,A2,B2,A3,B3,C;
int main()
read(T);
while(T--)
while(!A1.empty())A1.pop();while(!B1.empty())B1.pop();
while(!A2.empty())A2.pop();while(!B2.empty())B2.pop();
while(!A3.empty())A3.pop();while(!B3.empty())B3.pop();
while(!C.empty())C.pop();
read(n);read(K);read(L);int num=0;
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)read(b[i]);
for(int i=1;i<=n;i++)
A1.push(mkpr(a[i],i)),
B1.push(mkpr(b[i],i)),
C.push(mkpr(a[i]+b[i],i));
for(int i=1;i<=K;i++)
while(!A1.empty()&&visa[A1.top().sec])A1.pop();
while(!B1.empty()&&visb[B1.top().sec])B1.pop();
while(!A2.empty()&&!visa[A2.top().sec])A2.pop();
while(!B2.empty()&&!visb[B2.top().sec])B2.pop();
while(!A3.empty()&&!visa[A3.top().sec])A3.pop();
while(!B3.empty()&&!visb[B3.top().sec])B3.pop();
while(!C.empty()&&以上是关于[NOI2019]序列的主要内容,如果未能解决你的问题,请参考以下文章
洛谷 P1795 无穷的序列_NOI导刊2010提高(05)