[海军国际项目办公室]假人
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[海军国际项目办公室]假人相关的知识,希望对你有一定的参考价值。
假人
题目描述
题解
首先
30
p
t
s
30pts
30pts的暴力背包
d
p
dp
dp大概是很好想到的,但由于它的复杂度达到了
O
(
n
2
k
2
)
O\\left(n^2k^2\\right)
O(n2k2),显然是过不了的。
我们根据
k
i
⩽
2
,
3
k_{i}\\leqslant 2,3
ki⩽2,3的两个部分应该很容易想到通过反悔贪心来一步一步求出
t
∈
[
n
,
∑
k
i
]
t\\in[n,\\sum k_{i}]
t∈[n,∑ki]的答案。
但如果我们将
k
k
k的范围扩展到
5
5
5,那我们如果采用反悔贪心地话就需要讨论很多种情况,是很难处理的,果断放弃。
由于所有的假话组中都至少要有一个假话被选择,我们可以将
K
i
K_{i}
Ki都减一,此时我们的答案要求的范围也就变成了
[
0
,
∑
k
i
]
[0,\\sum k_{i}]
[0,∑ki]。
我们定义
d
p
i
dp_{i}
dpi表示选择总长度为
i
i
i的假话,我们可以得到的最大总权值和。
有结论,
d
p
i
+
12
−
d
p
i
⩾
d
p
i
+
24
−
d
p
i
+
12
dp_{i+12}-dp_{i}\\geqslant dp_{i+24}-dp_{i+12}
dpi+12−dpi⩾dpi+24−dpi+12,也就是说在模
12
12
12的意义下,原序列是一个凸包。
事实上着结论出题人也没有给出详尽的证明,但我们可以通过暴力对其进行验证。
在
C
2020
j
z
m
\\color{black}{C}\\color{red}{2020jzm}
C2020jzm的博客有尝试通过暴力对其进行检验的代码。
既然知道了这样的结论,我们可以想到通过分治的方法去求出我们的答案。
对于两个区间,我们可以对它们的
d
p
dp
dp值在模
12
12
12不同的编号下,通过闵可夫斯基和的方式进行求和,进而得到我们完整的区间的
d
p
dp
dp值,其实该方式有点类似于归并,根据极角的大小移动双指针。
我们记
K
=
12
K=12
K=12,由于是按
K
K
K分类合并的,我们总共要进行
K
2
K^2
K2不同类型,而每次合并是
O
(
k
n
K
)
O\\left(\\frac{kn}{K}\\right)
O(Kkn)。
分治合并后直接就可以得到整个区间的答案了。
时间复杂度 O ( K k n log n ) O\\left(Kkn\\log\\,n\\right) O(Kknlogn)
源码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
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>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){putchar('\\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
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&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,K[MAXN],a[MAXN][5],idx,siz[MAXN<<2];LL ans,dp[25][MAXN*4],g[MAXN*4];
int sakura(int rt,int l,int r){
if(l==r){
int rtp=++idx;siz[rt]=K[l];
for(int j=0;j<=K[l];j++)dp[rtp][j]=a[l][j];
return rtp;
}
int mid=l+r>>1,lsp,rsp,rtp;
lsp=sakura(lson,l,mid);rsp=sakura(rson,mid+1,r);siz[rt]=siz[lson]+siz[rson];
for(int i=0;i<12&&i<=siz[lson];i++)
for(int j=0;j<12&&j<=siz[rson];j++){
int k=i+j,i1=i,j1=j;g[k]=max(g[k],dp[lsp][i1]+dp[rsp][j1]);
while(i1+12<=siz[lson]&&j1+12<=siz[rson]){
if(dp[lsp][i1+12]-dp[lsp][i1]>dp[rsp][j1+12]-dp[rsp][j1])i1+=12;else j1+=12;
k+=12;g[k]=max(g[k],dp[lsp][i1]+dp[rsp][j1]);
}
while(i1+12<=siz[lson])i1+=12,k+=12,g[k]=max(g[k],dp[lsp][i1]+dp[rsp][j1]);
while(j1+12<=siz[rson])j1+=12,k+=12,g[k]=max(g[k],dp[lsp][i1]+dp[rsp][j1]);
}
for(int i=0;i<=siz[lson];i++)dp[lsp][i]=0;lsp=0;
for(int i=0;i<=siz[rson];i++)dp[rsp][i]=0;rsp=0;
idx-=2;rtp=++idx;for(int i=0;i<=siz[rt];i++)dp[rtp][i]=g[i],g[i]=0;
return rtp;
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
read(K[i]);K[i]--;
for(int j=0;j<=K[i];j++)read(a[i][j]);
}
int rt=sakura(1,1,n);
for(int i=0;i<siz[1];i++)printf("%lld ",dp[rt][i]);
printf("%lld\\n",dp[rt][siz[1]]);
return 0;
}
谢谢!!!
以上是关于[海军国际项目办公室]假人的主要内容,如果未能解决你的问题,请参考以下文章