CF1946F-树状数组优化DP,啊啊啊睡不着了

link:https://codeforces.com/contest/1946/problem/F
题意:给一个长度为 \(n\) 的排列 \(a\),有 \(q\) 个询问,每次问 \([l,r]\) 内有多少子序列 \(t_1,\dots,t_k\) 满足:

  • \(l\leq t_1<\dots<t_k\leq r\)
  • \(a_{t_1}|a_{t_2},\dots,a_{t_{k-1}}|a_{t_k}\)
    \(1\leq n,q\leq 10^6\),时限6秒

想来想去自己真是个大傻逼…居然在最后写的时候写了个假的复杂度,到最后2分钟才发现

\(f_i\) 表示强制以 \(a_i\) 结尾的子序列个数,考虑从后往前插入元素,插入到 \(a_l\) 的时候处理所有左端点为 \(l\) 的询问。

\[f_i=1+\sum_{j<i,a_j|a_i}f_j \]

每次插入一个 \(x=a_i\),需要更新所有值为 \(2x,3x\dots,\lfloor\frac{n}{x} \rfloor x\) 的位置的 \(f\) (如果这些位置比 \(i\) 大),而很明显 \(kx\) 会影响到所有形如 \(p\cdot kx\) 处的值,所以我们先倒着把贡献扣掉(按位置从后往前枚举倍数,每个倍数 \(kx\) 在自己的倍数处把自己的贡献扣掉)![[asset/Pasted image 20240323014539.png]]
然后再正着(按位置)从小到大,给倍数加上 \(a_i\) 的贡献(就是1),然后再更新自己后面的位置。
![[asset/Pasted image 20240323014650.png]]
上面这个过程如果用树状数组维护 \(f\) 的值的话,复杂度是多少呢?我们来算算
每个\(x\) 需要枚举倍数\(2x,\dots,\lfloor\frac{n}{x}\rfloor x\),每个倍数 \(kx\) 需要枚举 \(\lfloor n/(kx)\rfloor\) 个倍数,因此复杂度是

\[O(\log n\sum_{x=1}^n \sum_{k=1}^{n/x}\frac{n}{kx})=O(n\log n\cdot \sum_{x=1}^n \frac{1}{x} \log \frac{n}{x})=O(n\log^2 n \int_0^n \frac{\ln x}{x}dx)=O(n\log^3 n) \]

哈哈…复杂度寄了!(啊啊啊啊啊啊啊啊啊啊…还以为是2log的,赛时没想清楚就去写了,T了两次才去算复杂度)
这个过程是不是可以优化呢?没错!整个过程并不涉及到区间查询!只有单点修改和单点查询!!!
这时候要用什么数据结构呢?!?!?!?!
数组!!!!!!!!!
操作前先把 \(x\) 所有倍数的位置的 \(f\) 值用数组记录下来!!!
这样每次修改只需要 \(O(\log^2 n)\) 的时间,最后再把新的答案打回到树状数组上!
复杂度就是 \(O(n\log^2 n)\) 了!!!!!

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int N=1e6+5;
struct Fenwick{
    ll tr[N];
    int n;
    void init(int mx){
        n=mx;
        rep(i,0,n)tr[i]=0;
    }
    int lowbit(int x){return x&-x;}
    void modify(int x,ll v){
        for(;x<=n;x+=lowbit(x))tr[x]+=v;
    }
    ll Q(int x){
        ll ret=0;
        for(;x;x-=lowbit(x))ret+=tr[x];
        return ret;
    }
    ll query(int L,int R){return Q(R)-Q(L-1);}
}f;
int n,q,a[N],pos[N];
vector<vector<int>> mul;
ll ans[N],t[N];
struct Query{
    int l,r,idx;
    bool operator <(const Query &rhs)const{
        return l>rhs.l;
    }
}Q[N];
void add(int p,ll v){//(n/x)
    for(auto x:mul[p])t[x]+=v;
}
int main(){
    fastio;
    int tc;cin>>tc;
    while(tc--){
        cin>>n>>q;
        rep(i,1,n)cin>>a[i],pos[a[i]]=i;
        rep(i,1,q)cin>>Q[i].l>>Q[i].r,Q[i].idx=i;
        sort(Q+1,Q+q+1);
        int cur=0;
        f.init(n);
        mul=vector<vector<int>>(n+1);
        for(int i=n;i>=1;i--){
            f.modify(i,1);
            for(int j=a[i]+a[i];j<=n;j+=a[i])if(pos[j]>i)
                mul[i].push_back(pos[j]);

            sort(mul[i].begin(),mul[i].end(),[](int x,int y){return x>y;});

            for(auto x:mul[i])t[x]=f.query(x,x);
            for(auto x:mul[i])add(x,-t[x]);
            reverse(mul[i].begin(),mul[i].end());
            for(auto x:mul[i]){
                t[x]++;
                add(x,t[x]);
            }
            for(auto x:mul[i])f.modify(x,t[x]-f.query(x,x));

            while(cur+1<=q&&Q[cur+1].l==i){
                cur++;
                ans[Q[cur].idx]=f.query(Q[cur].l,Q[cur].r);
            }
        }
        rep(i,1,q)cout<<ans[i]<<' ';
        cout<<endl;
    }
    return 0;
}
posted @ 2024-03-23 01:54  yoshinow2001  阅读(12)  评论(0编辑  收藏  举报