s b sb sb 题,不多说
感觉不太好像,也不好证,不过发现了自己斜率优化不熟练的缺点
深刻认识到了自己的菜,感觉很典的题却连部分分都不会做
不说了, n f l s nfls nfls 纯 ∗ ∗ ** ∗∗,放大树分块题,狗都不补
不说了,直接 s e g m e n t − t r e e segment-tree segment−tree
#include
using namespace std;
const int N=200100;
int n,q,a[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
struct SegmentTree{
int seg[N<<2];
void build(int l,int r,int x,int type){
if(l==r){ seg[x]=(l&1)==type?a[l]:0;return;}
int mid=(l+r)>>1;build(l,mid,x<<1,type),build(mid+1,r,x<<1^1,type);
seg[x]=seg[x<<1]^seg[x<<1^1];
}
void modify(int l,int r,int x,int pos,int v){
if(l==r){ seg[x]=v;return;}
int mid=(l+r)>>1;
if(mid>=pos) modify(l,mid,x<<1,pos,v);
else modify(mid+1,r,x<<1^1,pos,v);
seg[x]=seg[x<<1]^seg[x<<1^1];
}
int query(int l,int r,int x,int L,int R){
if(L<=l&&r<=R) return seg[x];
int mid=(l+r)>>1;
if(mid>=L&&mid<R) return query(l,mid,x<<1,L,R)^query(mid+1,r,x<<1^1,L,R);
if(mid>=L) return query(l,mid,x<<1,L,R);
return query(mid+1,r,x<<1^1,L,R);
}
}sg0,sg1;
int main(){
freopen("orange.in","r",stdin);
freopen("orange.out","w",stdout);
n=read(),q=read();
for(int i=1;i<=n;i++) a[i]=read();
sg0.build(1,n,1,0),sg1.build(1,n,1,1);
while(q--){
int op=read(),x=read(),y=read();
if(op==1){
if(x&1) sg1.modify(1,n,1,x,y);
else sg0.modify(1,n,1,x,y);
}
else{
if((y-x+1)&1){
if(x&1) printf("%d\n",sg1.query(1,n,1,x,y));
else printf("%d\n",sg0.query(1,n,1,x,y));
}
else puts("0");
}
}
fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}
放在 T 2 T2 T2 感觉很困难
考虑一个构造做法是:
我们把 a i a_i ai 从大到小排序,如果我们砸开的椰子集合为 { b 1 , . . . , b m } \{b_1,...,b_m\} {b1,...,bm},那么最优的最坏情况下需要砸的次数为 ∑ i = 1 m ( b i − b i − 1 ) a b i \sum\limits_{i=1}^{m}(b_i-b_{i-1})a_{b_i} i=1∑m(bi−bi−1)abi
然后就可以列出朴素的 d p dp dp,然后用斜率优化,时间复杂度为 O ( n m ) O(nm) O(nm)
我发现自己的斜率优化很不熟练,感觉只会板子
当在下凸壳上查询点递减,只要用栈就可以了,而不是记录 h e a d , t a i l head,tail head,tail 指针
为什么这个构造是对的话可以考虑每次我们的目标是砸开某一个椰子 x x x ,那么最坏情况就是前面的所有椰子都被砸一遍,所以我们取阈值 a x a_x ax 最优
#include
#define int long long
using namespace std;
const int N=2100;
int a[N],f[N][N];
int q[N],tt;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void work(){
int n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+n+1,greater<int>());
memset(f,0x3f,sizeof(f));
int inf=f[0][0];
f[0][0]=0;
for(int j=1;j<=m;j++){
q[1]=0,tt=1;
for(int i=1;i<=n;i++){
while(tt>=2&&f[q[tt]][j-1]-f[q[tt-1]][j-1]>(__int128)a[i]*(q[tt]-q[tt-1])) tt--;
int k=q[tt];
if(f[k][j-1]!=inf) f[i][j]=f[k][j-1]+a[i]*(i-k);
while(tt>=2&&(__int128)(f[q[tt]][j-1]-f[q[tt-1]][j-1])*(i-q[tt])>(__int128)(f[i][j-1]-f[q[tt]][j-1])*(q[tt]-q[tt-1])) tt--;
q[++tt]=i;
}
}
int ans=inf;
for(int i=1;i<=n;i++) ans=min(ans,f[i][m]);
printf("%lld\n",ans);
}
signed main(){
freopen("protect.in","r",stdin);
freopen("protect.out","w",stdout);
int T=read();
while(T--) work();
fprintf(stderr,"%d ms\n",int64_t(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}
感觉是一道很经典的题
我们首先考虑 n m l o g nmlog nmlog 怎么做
先把每一件装备从大到小排序
我们可以用堆来维护所有可以的当前最大选择,不妨令选择为 ( c 1 , c 2 , . . . , c n ) (c_1,c_2,...,c_n) (c1,c2,...,cn)
考虑如何拓展新的方案,我们可以把任意一个 c i + 1 c_i+1 ci+1,然后把这个选择放进去,要注意去重,时间复杂度 O ( n m l o g n ) O(nmlogn) O(nmlogn)
考虑拓展的形态是一个 D A G DAG DAG,这样会导致去重的复杂度瓶颈 <p
我们考虑把 D A G DAG DAG 变成一棵树,即给每个状态钦定唯一的转移顺序
我们令三元组 ( v a l , p , c ) (val,p,c) (val,p,c) 表示权值和为 v a l val val,现在修改到第 p p p 个,后面都选的是第 1 1 1 个,第 p p p 个选的是第 c c c 个,且后面不修改 < p
考虑转移:
思考一下可以发现这样可以不重不漏的表示出所有的状态(感觉不是见过很难想)
考虑这样转移仍然是 O ( n m l o g ) O(nmlog) O(nmlog) 的
不难想到如下的等价转移方式:
时间复杂度 O ( m l o g n ) O(mlogn) O(mlogn)
#include
#define pb push_back
#define int long long
using namespace std;
const int N=300100,P=20190816170251;
struct Node{
int val,p,x;
bool operator <(const Node &o)const{ return val<o.val;}
};
int n,m,ans[N];
priority_queue<Node> pq;
vector<int> G[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
bool cmp(vector<int> &A,vector<int> &B){ return A[1]-A[0]>B[1]-B[0];}
signed main(){
freopen("contain.in","r",stdin);
freopen("contain.out","w",stdout);
n=read(),m=read();
int res=0;
for(int i=1;i<=n;i++){
int cnt=read();
if(cnt==1) res+=read(),i--,n--;
else{
while(cnt--) G[i].pb(read());
sort(G[i].begin(),G[i].end(),greater<int>());
}
}
sort(G+1,G+n+1,cmp);
int mx=res;
for(int i=1;i<=n;i++) mx+=G[i][0];
pq.push({mx,1,1});
for(int i=1;i<=m;i++){
Node cur=pq.top();ans[i]=cur.val;pq.pop();
if(cur.x<G[cur.p].size()){
pq.push({cur.val+G[cur.p][cur.x]-G[cur.p][cur.x-1],cur.p,cur.x+1});
}
if(cur.p<n){
if(cur.x>1) pq.push({cur.val+G[cur.p+1][1]-G[cur.p+1][0],cur.p+1,2});
if(cur.x==2) pq.push({cur.val+G[cur.p+1][1]-G[cur.p+1][0]+G[cur.p][0]-G[cur.p][1],cur.p+1,2});
}
}
// for(int i=1;i<=m;i++) cout<
int ANS=0;
for(int i=m,pw=1;i>=1;i--,pw=pw*23333%P) ANS=(ANS+(__int128_t)pw*ans[i]%P)%P;
printf("%lld\n",ANS);
fprintf(stderr,"%d ms\n",int64_t(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}