2.17

· · 个人记录

A. CF1710D Recover the Tree

容易想到按照区间大小合并,因为 [l,r-1] 的限制显然比 [l,r] 的限制更严格,参考 lr-2 的链后接上 r 再接上 r-1,此时不满足 [l,r-1] 但是满足 [l,r]

容易发现,在任意时刻,所有的连通块一定呈区间的形式。

于是我们先求出 l,r 所在的区间。

如果它们在同一个区间里,那么它们就已经在一个连通块里了,此时不操作即可。

否则我们我们假设 l 在区间 i_1r 在区间 i_k,区间 i[L_i,R_i]

k=2,那么只需要向 l,r 连边即可,否则无论怎么连,[l,r] 都会少元素。

先考虑一个简单的情况,只有 [1,n] 为联通块(单元素区间不考虑),那么我们只需要让 13,\cdots,n 连边,2n 连边即可。

那么类似地,我们只需要让 lr 连边,R_{i_2}r 连边,R_{i_3,\cdots,i_{k-1}}l 连边即可。

注意单次操作完之后区间会合并,上述操作只涉及到了 R,那么相当于我们要维护一个能支持动态删除 R 的数据结构,vector 即可,因为每个元素只会删除一次,单次删除最多 O(n),所以总的复杂度为 O(n^2)。然后你的所有的迭代器在遍历过一次之后都会删除,所以这里是 O(n) 的。

其实你会发现讨论了那么多,代码其实很简单。

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N=2005;
int T,n;
char c[N][N];
vector<int> v;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--){
        cin>>n;
        for(int l=1;l<=n;++l) for(int r=l;r<=n;++r) cin>>c[l][r];
        v.clear(),v.shrink_to_fit(),v.emplace_back(0);
        for(int i=1;i<=n;++i) v.emplace_back(i);
        for(int len=1;len<=n;++len) for(int l=1,r=len;r<=n;++l,++r){
            if(c[l][r]=='1'){
                auto itl=lower_bound(v.begin(),v.end(),l),itr=lower_bound(v.begin(),v.end(),r);
                for(auto it=itl;it<itr;++it){
                    if(it==itl) cout<<l<<' '<<r<<endl;
                    else if(it==itl+1) cout<<r<<' '<<(*it)<<endl;
                    else cout<<l<<' '<<(*it)<<endl;
                }
                v.erase(itl,itr);
            }
        }
    }
    return 0;
}

D. CF1844G Tree Weights

题目等价于给出 dep_i+dep_{i-1}-2dep_{LCA(i,i-1)}=dis_i,显然直接解方程是不显示的。

注意到 LCA(i,i-1) 这一坨的系数是 2,我们先对原式模 2,就能得到 dep_i 的奇偶性。注意到 2\Delta\bmod 2^p=2(\Delta\bmod 2^{p-1}),那么我们就在得到模 2 之后带入模 4 的式子算即可,此时 2dep_{LCA(i,i-1)} 是已知值,那么就可以继续递归求解。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,d[N],siz[N],son[N],dep[N],fa[N],top[N],lca[N],f[2][N],u[N],v[N];
vector<int> G[N];
void dfs1(int u){
    siz[u]=1;
    for(auto v:G[u]){
        if(v==fa[u]) continue;
        dep[v]=dep[u]+1,fa[v]=u;
        dfs1(v);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int t){
    top[u]=t;
    if(son[u]) dfs2(son[u],t);
    for(auto v:G[u]){
        if(v==son[u]||v==fa[u]) continue;
        dfs2(v,v);
    }
}
int LCA(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]>dep[top[v]]) swap(u,v);
        v=fa[top[v]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    return u;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<n;++i){
        cin>>u[i]>>v[i];
        G[u[i]].emplace_back(v[i]);
        G[v[i]].emplace_back(u[i]);
    }
    for(int i=2;i<=n;++i) cin>>d[i];
    dfs1(1),dfs2(1,1);
    for(int i=1;i<n;++i) if(dep[u[i]]>dep[v[i]]) swap(u[i],v[i]);
    for(int i=2;i<=n;++i) lca[i]=LCA(i,i-1);
    for(int i=1;i<=60;++i){
        f[i&1][1]=0;
        for(int j=2;j<=n;++j) f[i&1][j]=(d[j]-f[i&1][j-1]+2*f[i-1&1][lca[j]]+(1ll<<i))%(1ll<<i);
    }
    for(int i=1;i<n;++i) if(f[0][v[i]]<=f[0][u[i]]){
        cout<<-1<<endl;
        return 0;
    }
    for(int i=2;i<=n;++i) if(f[0][i]+f[0][i-1]-(f[0][lca[i]]<<1)!=d[i]){
        cout<<-1<<endl;
        return 0;
    }
    for(int i=1;i<n;++i) cout<<f[0][v[i]]-f[0][u[i]]<<endl;
    return 0;
}

C. P6860 象棋与马

先来考虑 p(a,b) 的性质。

首先能到达整个棋盘等价于能到达 (0,1),那么我们设 4 种移动状态((+a,+b),(+a,-b),(-a,+b),(-a,-b))次数分别为 a_1,a_2,a_3,a_4,那么有:

\begin{cases}a(a_1+a_2-(a_3+a_4))=0\\b(a_1+a_3-(a_2+a_4))=1\end{cases}

这个系数似乎观察不出什么规律,我们令 x=a_1-a_4,y=a_2-a_3,然后拆括号,化简有:

\begin{cases}a(x+y)=0\\b(x-y)=1\end{cases}

两边相加知:

那么等价于 $ax+by=1$ 这个方程有**奇偶性相同的解**,那么首先就有 $\gcd(a,b)=1$。如何体现奇偶性呢,首先注意到 $x,y$ 显然不会同时为偶数,因此它们都是奇数,我们令 $x=2X+1,y=2Y+1$,那么有:$(2X+1)a+(2Y+1)b=2(Xa+Yb)+(a+b)=1$。这样就很清晰了,因为第一坨是偶数,所以第二坨一定是奇数。 那么问题就等价于 $f(n,n)=\sum\limits_{a=1}^n\sum\limits_{b=1}^n[\gcd(a,b)=1\land 2\nmid(a+b)]$。不妨假设 $a$ 为偶数,那么 $b$ 就是奇数,最终答案 $\div 2$ 即可,注意到 $\gcd(2n,2m+1)=\gcd(n,2m+1)$,那么有 $f(n,n)=\sum\limits_{a=1}^{\lfloor\frac{n}{2}\rfloor}\sum\limits_{b=1}^n[\gcd(a,b)\land 2\nmid b]

首先有个非常简单的容斥:

f(n,n)=\sum\limits_{a=1}^{\lfloor\frac{n}{2}\rfloor}\sum\limits_{b=1}^m[\gcd(a,b)=1]-\sum\limits_{a=1}^{\lfloor\frac{n}{2}\rfloor}\sum\limits_{b=1}^m[\gcd(a,b)=1\land 2\mid b]

因为 b 是偶数所以 a 显然不是偶数,那么我们继续,令 s(n,m)=\sum\limits_{a=1}^{n}\sum\limits_{b=1}^m[\gcd(a,b)=1],有:

f(n,m)=s(\lfloor\dfrac{n}{2}\rfloor,m)-\sum\limits_{a=1}^{\lfloor\frac{n}{2}\rfloor}\sum\limits_{b=1}^m[\gcd(a,b)=1\land 2\nmid a\land 2\mid b]

你会发现后边这一坨其实就是等价于 f 的一个子问题,那么有:f(n,m)=s(\lfloor\dfrac{n}{2}\rfloor,m)-f(m,\lfloor\dfrac{n}{2}\rfloor)

现在还剩下 s 的求解,套用莫反知:

s(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^m[\gcd(i,j)=1]\\=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^m\sum\limits_{d\mid \gcd(a,b)}\mu(d)

考虑枚举 \gcd,有:

s(n,m)=\sum\limits_{d=1}^{\min\{n,m\}}\mu(d)\lfloor\dfrac{n}{d}\rfloor\lfloor\dfrac{m}{d}\rfloor

整除分块即可,对于 \mu(d) 的前缀和可以用杜教筛求解,具体的时间复杂度其实就是一个多次查询的杜教筛,应该是 O(n^{\frac{2}{3}}) 的。

#include<bits/stdc++.h>
#define int unsigned long long
#define endl '\n'
using namespace std;
const int N=1e7+5,INF=1e7;
int T,n,prime[N],cnt,Smu[N];
bool flg[N];
unordered_map<int,int> mp_mu;
void Euler(int n){
    Smu[1]=1,flg[1]=1;
    for(int i=2;i<=n;++i){
        if(!flg[i]){
            Smu[i]=-1;
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=n;++j){
            flg[i*prime[j]]=1;
            if(i%prime[j]==0){
                Smu[i*prime[j]]=0;
                break;
            }
            Smu[i*prime[j]]=-Smu[i];
        }
    }
    for(int i=2;i<=n;++i) Smu[i]+=Smu[i-1];
}
int Sum_mu(int n){
    if(n<=INF) return Smu[n];
    if(mp_mu.count(n)) return mp_mu[n];
    int ans=1;
    for(int l=2,r;l<=n;l=r+1){
        r=n/(n/l);
        ans-=(r-l+1)*Sum_mu(n/l);
    }
    return mp_mu[n]=ans;
}
int s(int n,int m){
    int ans=0;
    for(int l=1,r;l<=min(n,m);l=r+1){
        r=min(n/(n/l),m/(m/l));
        ans+=(Sum_mu(r)-Sum_mu(l-1))*(n/l)*(m/l);
    }
    return ans;
}
int f(int n,int m){
    if(n==1) return 0;
    if(m==1) return n/2;
    return s(n/2,m)-f(m,n/2);
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    Euler(INF);
    cin>>T;
    while(T--){
        cin>>n;
        cout<<f(n,n)*2<<endl;
    }
    return 0;
}

E. CF878D Magic Breeding

然后的话,我们显然不能具体下放到每个值来考虑偏序关系,我们考虑把这个问题下放到判定,也就是说初始化的时候只要包含 $i$ 这个状态就行,然后查询的时候查询 $\ge a_i$ 至少需要的状态条件。 注意到 $1$ 操作取 $\max$,那么只需要有一个是大于等于的它就是大于等于的,相当于或;对于 $2$ 操作取 $\min$,必须要两个数都大于等于才会大于等于,因此是与。 所以上述过程可以用 `bitset` 来加速这个过程。 ```cpp #include<bits/stdc++.h> #define endl '\n' using namespace std; const int N=1e5+20; int a[13][N],tot,n,k,q,b[13]; bitset<4096> f[N]; int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>k>>q; for(int i=1;i<=k;++i){ for(int j=1;j<=n;++j) cin>>a[i][j]; for(int s=0;s<(1<<k);++s) if(s&(1<<i-1)) f[i][s]=1; else f[i][s]=0; } tot=k; while(q--){ int op,x,y; cin>>op>>x>>y; if(op==1) f[++tot]=(f[x]|f[y]); else if(op==2) f[++tot]=(f[x]&f[y]); else{ int res=-0x3f3f3f3f; for(int i=1;i<=k;++i) b[i]=a[i][y]; sort(b+1,b+k+1,greater<int>()); for(int i=1;i<=k;++i){ int state=0; for(int j=1;j<=k;++j) if(a[j][y]>=b[i]) state|=(1<<j-1); if(f[x][state]){ cout<<b[i]<<endl; break; } } } } return 0; } ```