[题解] 杂题记录(2021/10/30)

IOI2018 Meetings

题目

链接

简答

设状态 \(f_{i,j}\) 表示区间 \(\left[i,j\right]\) 的最小代价,那么朴素区间dp是 \(\mathcal{O}\left(n^2\right)\)

考虑建出这个序列的笛卡尔树,然后处理到节点 \(u\) 时,假设它是区间 \(\left[l,r\right]\) 里的最大值,那么考虑建两棵线段树,维护 \(f_{l,u-1},f_{l+1,u-1}\cdots f_{u-1,u-1},f_{u+1,r},f_{u+2,r}\cdots f_{r,r}\) 以及 \(f_{l,l},f_{l,l+1}\cdots f_{l,u-1},f_{u+1,u+1},f_{u+1,u+2}\cdots f_{u+1,r}\)

对于一个区间 \(\left[l,r\right]\) ,设 \(u=\operatorname{LCA}\left(l,r\right)\) ,那么当决策点 \(\le u\) 时 ,\(u,u+1,\cdots r\) 的代价都是 \(v_u\) ,当决策点 \(\ge u\) 时,\(l,l+1,\cdots u\) 的代价也都是 \(v_u\)

考虑如何快速从 \(u\) 转移到 \(fa_u\)

假设 \(u\)\(v=fa_u\) 的左子树 ,\(u\) 代表的区间是 \(\left[l,v-1\right]\)\(v\) 代表的区间是 \(\left[l,r\right]\)

那么 \(f_{k,v-1}\rightarrow \min\left(f_{k,u-1}+\left(v-u\right)v_u,f_{u+1,v-1}+\left(u-k+1\right)v_u\right)\) ,其余同理,即线段树上加等差数列,区间取最小值

由于 \(f_{k,v-1}\) 是单调的,所以直接线段树上二分即可

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <tuple>
using namespace std;
typedef long long ll;
constexpr int N=750010;
struct segT{
    ll tgadd[N<<2],tgarr[N<<2][2],fst[N<<2],lst[N<<2];
    bool hag[N<<2],harg[N<<2];
    void updarr(int u,ll n1,ll n2,int l,int r){harg[u]=true,hag[u]=false,tgadd[u]=0;fst[u]=n1,lst[u]=n1+n2*(r-l);tgarr[u][0]=n1,tgarr[u][1]=n2;}
    void updadd(int u,ll n1){hag[u]=true;fst[u]+=n1,lst[u]+=n1,tgadd[u]+=n1;}
    void pushdown(int u,int l,int r){
        if(l==r) return;int mid=(l+r)>>1;
        if(harg[u]) updarr(u<<1,tgarr[u][0],tgarr[u][1],l,mid),updarr(u<<1|1,tgarr[u][0]+tgarr[u][1]*(mid-l+1),tgarr[u][1],mid+1,r),harg[u]=false;
        if(hag[u]) updadd(u<<1,tgadd[u]),updadd(u<<1|1,tgadd[u]),hag[u]=false,tgadd[u]=0;
    }
    void segadd(int u,int cl,int cr,int ql,int qr,ll val){
        pushdown(u,cl,cr);if(cl>=ql && cr<=qr){updadd(u,val);return;}int mid=(cl+cr)>>1;
        if(ql<=mid) segadd(u<<1,cl,mid,ql,qr,val);if(qr>mid) segadd(u<<1|1,mid+1,cr,ql,qr,val);fst[u]=fst[u<<1],lst[u]=lst[u<<1|1];
    }
    void segarr(int u,int cl,int cr,int ql,int qr,ll val1,ll val2){
        pushdown(u,cl,cr);if(cl>=ql && cr<=qr){updarr(u,val1+(cl-ql)*val2,val2,cl,cr);return;}int mid=(cl+cr)>>1;
        if(ql<=mid) segarr(u<<1,cl,mid,ql,qr,val1,val2);if(qr>mid) segarr(u<<1|1,mid+1,cr,ql,qr,val1,val2);fst[u]=fst[u<<1],lst[u]=lst[u<<1|1];
    }
    ll query_s(int u,int cl,int cr,int pos){pushdown(u,cl,cr);if(cl==cr) return fst[u];int mid=(cl+cr)>>1;return pos<=mid?query_s(u<<1,cl,mid,pos):query_s(u<<1|1,mid+1,cr,pos);}
    void upd_left(int u,int cl,int cr,int ql,int qr,ll val1,ll val2){
        pushdown(u,cl,cr);int mid=(cl+cr)>>1;
        if(cl>=ql && cr<=qr){
            ll st=(cl-ql)*val2+val1,ed=(cr-ql)*val2+val1;
            if(lst[u]>ed && fst[u]>st) updarr(u,st,val2,cl,cr);
            else if(fst[u]>st) upd_left(u<<1,cl,mid,ql,qr,val1,val2),upd_left(u<<1|1,mid+1,cr,ql,qr,val1,val2),fst[u]=fst[u<<1],lst[u]=lst[u<<1|1];
            return;
        }
        if(ql<=mid) upd_left(u<<1,cl,mid,ql,qr,val1,val2);if(qr>mid) upd_left(u<<1|1,mid+1,cr,ql,qr,val1,val2);fst[u]=fst[u<<1],lst[u]=lst[u<<1|1];
    }
    void upd_right(int u,int cl,int cr,int ql,int qr,ll val1,ll val2){
        pushdown(u,cl,cr);int mid=(cl+cr)>>1;
        if(cl>=ql && cr<=qr){
            ll st=(cl-ql)*val2+val1,ed=(cr-ql)*val2+val1;
            if(lst[u]>ed && fst[u]>st) updarr(u,st,val2,cl,cr);
            else if(lst[u]>ed) upd_right(u<<1,cl,mid,ql,qr,val1,val2),upd_right(u<<1|1,mid+1,cr,ql,qr,val1,val2),fst[u]=fst[u<<1],lst[u]=lst[u<<1|1];
            return;
        }
        if(ql<=mid) upd_right(u<<1,cl,mid,ql,qr,val1,val2);if(qr>mid) upd_right(u<<1|1,mid+1,cr,ql,qr,val1,val2);fst[u]=fst[u<<1],lst[u]=lst[u<<1|1];
    }
}TL,TR;//TL from left to right, TR from right to left
int stk[N],arr[N],ch[N][2],stktop,n,q,son[N],top[N],dep[N],fa[N];
void dfs1(int u,int ff,int dp,int l,int r,int tp){
    dep[u]=dp,son[u]=((u-l)>(r-u))?ch[u][0]:ch[u][1],top[u]=tp,fa[u]=ff;
    if(ch[u][0]) dfs1(ch[u][0],u,dp+1,l,u-1,(ch[u][0]==son[u])?tp:ch[u][0]);
    if(ch[u][1]) dfs1(ch[u][1],u,dp+1,u+1,r,(ch[u][1]==son[u])?tp:ch[u][1]);
    // printf("%d %d %d %d %d\n",u,ch[u][0],ch[u][1],dep[u],top[u]);
}
int lca(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]>dep[v]?v:u;
}
vector<tuple<int,int,int> > qrys[N];ll ans[N];
void dfs_calc(int u,int l,int r){
    if(ch[u][0]) dfs_calc(ch[u][0],l,u-1);if(ch[u][1]) dfs_calc(ch[u][1],u+1,r);
    if(ch[u][0]){
        int v=ch[u][0];TL.segadd(1,1,n,v,u-1,1ll*arr[v]*(v-l+1));TR.segadd(1,1,n,l,v,1ll*arr[v]*(u-v));
        ll vall=((l<v)?TL.query_s(1,1,n,v-1):0),valr=((v<u-1)?TR.query_s(1,1,n,v+1):0);
        TL.upd_left(1,1,n,v,u-1,vall+arr[v],arr[v]);TR.upd_right(1,1,n,l,v,valr+1ll*arr[v]*(v-l+1),-arr[v]);
        // printf("left %d %d %d\n",u,l,r);
        // for(int i=1;i<=n;++i) printf("(%lld,%lld) ",TL.query_s(1,1,n,i),TR.query_s(1,1,n,i));printf("\n");
    }
    if(ch[u][1]){
        int v=ch[u][1];TL.segadd(1,1,n,v,r,1ll*arr[v]*(v-u));TR.segadd(1,1,n,u+1,v,1ll*arr[v]*(r-v+1));
        ll vall=((u+1<v)?TL.query_s(1,1,n,v-1):0),valr=((v<r)?TR.query_s(1,1,n,v+1):0);
        TL.upd_left(1,1,n,v,r,vall+arr[v],arr[v]);TR.upd_right(1,1,n,u+1,v,valr+1ll*arr[v]*(v-u),-arr[v]);
    }
    // printf("%d %d %d\n",u,l,r);
    // for(int i=1;i<=n;++i) printf("(%lld,%lld) ",TL.query_s(1,1,n,i),TR.query_s(1,1,n,i));printf("\n");
    for(auto qry:qrys[u]) ans[get<2>(qry)]=min(1ll*arr[u]*(get<1>(qry)-u+1)+TR.query_s(1,1,n,get<0>(qry)),1ll*arr[u]*(u-get<0>(qry)+1)+TL.query_s(1,1,n,get<1>(qry)));
}
int main(){
    // freopen("meetings.in","r",stdin);
    scanf("%d %d",&n,&q);for(int i=1;i<=n;++i) scanf("%d",&arr[i]);
    stk[++stktop]=1;
    for(int i=2;i<=n;++i){
        while(stktop && arr[stk[stktop]]<arr[i]) ch[i][0]=stk[stktop],--stktop;
        if(stktop) ch[stk[stktop]][1]=i;stk[++stktop]=i;
    }
    dfs1(stk[1],0,1,1,n,stk[1]);
    for(int i=1,l,r;i<=q;++i) scanf("%d %d",&l,&r),++l,++r,qrys[lca(l,r)].push_back(make_tuple(l,r,i))/*,printf("%d\n",lca(l,r))*/;
    dfs_calc(stk[1],1,n);
    for(int i=1;i<=q;++i) printf("%lld\n",ans[i]);
    return 0;
}

CF1326F Wise Men

题目

链接

简答

这题不会做,观察题解的

考虑把 \(0\) 的限制去掉然后容斥。

先状压dp计算出 \(f_{S}\) ,表示一段连续的长度为 \(\left|S\right|\) 的 1,且点集为 \(S\) 的路径的方案数

然后直接暴力枚举 \(n\) 的划分作为每一段 \(1\) 的长度,\(FWT\) 卷积即可

注意到此时 \(\sum\left| S\right| = n\) ,所以直接取 \(FWT\)\(x_{\left[n\right]}\) 的系数即可保证 \(\cup S=\left[n\right]\) ,且任意两个集合交集为空

代码

#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
typedef long long ll;
constexpr int N=18;
map<vector<int>,int> ans;
int n,cnt;ll pre[N+1][(1<<N)+N],f[N][(1<<N)+N];
bool mp[N+1][N+1];char s[N+1];
vector<int> nums[N+1];
vector<int> cur;
vector<int> mm[2010];
ll fnl[(1<<N)+N],tmp[(1<<N)+N];
void fwt(ll *p,int len,int V){for(int l=2;l<=len;l<<=1) for(int mid=l>>1,j=0;j<len;j+=l) for(int i=0;i<mid;++i) p[i+j+mid]+=V*p[i+j];}
void dfs(int pos,int sum){
    if(!pos){if(sum==n) ++cnt,ans[cur]=cnt;return;}
    int lim=(n-sum)/pos;
    for(int i=0;i<=lim;++i) dfs(pos-1,sum+i*pos),cur.push_back(pos);
    for(int i=lim;i>=0;--i) cur.pop_back();
}
ll darr[(1<<N)+N];
ll st[N+1][(1<<N)+N];
void dfs2(int pos,int sum){
    if(!pos){if(sum==n){int idx=ans[cur];memcpy(tmp,darr,sizeof(tmp));fwt(tmp,(1<<n),-1);/*for(int i=0;i<(1<<n);++i) printf("%lld ",tmp[i]);printf("\n");*/
    for(int val:mm[idx]) fnl[val]=tmp[(1<<n)-1];}return;}
    int lim=(n-sum)/pos;
    dfs2(pos-1,sum);
    if(lim>=1){
        memcpy(st[pos],darr,sizeof(st[pos]));
        for(int i=1;i<=lim;++i){
            for(int j=0;j<(1<<n);++j) darr[j]*=pre[pos][j];cur.push_back(pos);
            dfs2(pos-1,sum+i*pos);
        }
        memcpy(darr,st[pos],sizeof(darr));for(int i=lim;i;--i) cur.pop_back();
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%s",s+1);
        for(int j=1;j<=n;++j) mp[i][j]=(s[j]-'0')==1;
    }
    for(int i=1;i<=n;++i) f[i][1<<(i-1)]=1;
    for(int i=1;i<(1<<n);++i) nums[__builtin_popcount(i)].push_back(i);
    for(int k=1;k<=n;++k) for(int val:nums[k])
    for(int i=1;i<=n;++i) if((val>>(i-1))&1) for(int j=1;j<=n;++j) if((val>>(j-1))&1 && i!=j && mp[i][j]) f[i][val]+=f[j][val^(1<<(i-1))];
    // for(int i=1;i<=n;++i){printf("%d:\n",i);for(int j=0;j<(1<<n);++j) printf("%lld ",f[i][j]);printf("\n");}
    for(int i=1;i<=n;++i){for(int val:nums[i]) for(int j=1;j<=n;++j) pre[i][val]+=f[j][val];
    fwt(pre[i],(1<<n),1);/*printf("%d:\n",i);for(int j=0;j<(1<<n);++j) printf("%lld ",pre[i][j]);printf("\n");*/}
    dfs(n,0);vector<int> dc;
    // printf("%d\n",cnt);
    for(int i=0;i<(1<<(n-1));++i){
        dc.clear();for(int j=0,ct=1;j<n;++j){if((i>>j)&1) ++ct;else dc.push_back(ct),ct=1;}
        sort(dc.begin(),dc.end(),[](auto a,auto b){return a>b;});
        mm[ans[dc]].push_back(i);
        // printf("%d %d\n",i,ans[dc]);
    }
    for(int i=0;i<(1<<n);++i) darr[i]=1;
    dfs2(n,0);//for(int i=0;i<(1<<(n-1));++i) printf("%lld ",fnl[i]);printf("\n");
    reverse(fnl,fnl+(1<<(n-1)));fwt(fnl,(1<<(n-1)),-1);reverse(fnl,fnl+(1<<(n-1)));
    for(int i=0;i<(1<<(n-1));++i) printf("%lld ",fnl[i]);printf("\n");
    return 0;
}
posted @ 2021-10-30 13:45  aixiaoyaowudi  阅读(34)  评论(0编辑  收藏  举报