【考试总结】2022-05-04

路人女主

势不两立的串对与以下两种形成双射(下述 ABC 是其六种轮换之一):

  • S=ABABABAT 的奇数位置是 C,偶数位置是 A/B

  • S,T 的偶数位置是 C,奇数位置满足存在一个 k 使得 s 的前 k 个奇数位置为 A,后面的奇数位置为 B,而 t 恰好相反:前 k 个奇数位置为 B,后面的奇数位置为 A

image

Code Display
char s[N<<1],t[N<<1];
int ans,n;
bool sufs[N<<1],suft[N<<1];
inline bool check(char A,char B){return A==B||A=='?';}
inline void solve(char A,char B,char C){
    bool Fail=0;
    int m=n<<1|1;
    for(int i=1;i<=m&&!Fail;++i){
        if((i&1)&&!check(s[i],A)) Fail=1;
        if(!(i&1)&&!check(s[i],B)) Fail=1;
    }
    for(int i=1;i<=m&&!Fail;i+=2) if(!check(t[i],C)) Fail=1;
    for(int i=2;i<=m&&!Fail;i+=2) if(!check(t[i],A)&&!check(t[i],B)) Fail=1;
    if(!Fail){
        int cnt=0;
        for(int i=2;i<=m;i+=2) cnt+=t[i]=='?'; 
        ckadd(ans,ksm(2,cnt));
    }else Fail=0;
    
    for(int i=1;i<=m&&!Fail;++i){
        if((i&1)&&!check(t[i],A)) Fail=1;
        if(!(i&1)&&!check(t[i],B)) Fail=1;
    }
    for(int i=1;i<=m&&!Fail;i+=2) if(!check(s[i],C)) Fail=1;
    for(int i=2;i<=m&&!Fail;i+=2) if(!check(s[i],A)&&!check(s[i],B)) Fail=1;
    if(!Fail){
        int cnt=0;
        for(int i=2;i<=m;i+=2) cnt+=s[i]=='?'; 
        ckadd(ans,ksm(2,cnt));
    }else Fail=0;
    
    for(int i=2;i<=m&&!Fail;i+=2) if(!check(s[i],C)||!check(t[i],C)) Fail=1;

    sufs[m+2]=suft[m+2]=1;
    for(int i=m;i>=1;i-=2){
        sufs[i]=sufs[i+2]&check(s[i],B);
        suft[i]=suft[i+2]&check(t[i],A);
    }
    for(int i=1;i<m;i+=2){
        Fail|=!check(s[i],A);
        Fail|=!check(t[i],B);
        if(Fail) break;
        if(sufs[i+2]&&suft[i+2]) ckadd(ans,1);
    }
    Fail|=!check(s[m],A);
    Fail|=!check(t[m],B);
    ckdel(ans,!Fail);
    return ;
}
signed main(){
    freopen("a.in","r",stdin); freopen("a.out","w",stdout);
    int T=read(); while(T--){        
        n=read(); ans=0;
        scanf("%s%s",s+1,t+1);
        solve('C','B','A');
        solve('C','A','B');
        solve('B','C','A');
        solve('B','A','C');
        solve('A','B','C');
        solve('A','C','B');
        print(ans);
    }
    return 0;
}

铃原露露

对于树上三个点 z=lca(x,y) 那么如果 az<ax<ay 那么对于 l(az,ax],r[ay,n] 这样子的区间 [l,r] 都是非法的,类似的 ax<ay<az 的结构会导致 l[1,ax],r[ay,az) 不合法

那么可以使用启发式合并维护所有的 ax 想要找到上面描述的矩形利用覆盖关系所需要的就是每个元素在其它子树里面的前驱后继

剩下的工作是一个线段树维护历史为 0 的次数的和,而这是平凡的

Code Display
const int N=2e5+10;
set<int> sub[N];
int n,Q;
struct Segment_Tree{
    #define ls p<<1
    #define rs p<<1|1
    #define lson p<<1,l,mid
    #define rson p<<1|1,mid+1,r
    int num[N<<2],sum[N<<2],Mn[N<<2];
    int plu[N<<2],tim[N<<2];
    inline void push_up(int p){
        sum[p]=sum[ls]+sum[rs];
        Mn[p]=min(Mn[ls],Mn[rs]);
        num[p]=0;
        if(Mn[p]==Mn[ls]) num[p]+=num[ls];
        if(Mn[p]==Mn[rs]) num[p]+=num[rs];
        return ;
    }
    inline void push_add(int p,int v){plu[p]+=v,Mn[p]+=v;}
    inline void push_tim(int p,int v){sum[p]+=num[p]*v,tim[p]+=v;}
    inline void push_down(int p){
        if(tim[p]){
            int Min=min(Mn[ls],Mn[rs]);
            if(Min==Mn[ls]) push_tim(ls,tim[p]);
            if(Min==Mn[rs]) push_tim(rs,tim[p]);
            tim[p]=0;
        }
        if(plu[p]){
            push_add(ls,plu[p]);
            push_add(rs,plu[p]);
            plu[p]=0;
        }
        return ;
    }
    inline void upd(int st,int ed,int v,int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return push_add(p,v);
        int mid=(l+r)>>1; push_down(p);
        if(st<=mid) upd(st,ed,v,lson);
        if(ed>mid) upd(st,ed,v,rson);
        return push_up(p);
    }
    inline int query(int st,int ed,int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return sum[p];
        int mid=(l+r)>>1,res=0; push_down(p);
        if(st<=mid) res+=query(st,ed,lson);
        if(ed>mid) res+=query(st,ed,rson);
        return res;
    }
    inline void incr(int st,int ed,int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return push_tim(p,Mn[p]==0);
        int mid=(l+r)>>1; push_down(p);
        if(st<=mid) incr(st,ed,lson);
        if(ed>mid) incr(st,ed,rson);
        return push_up(p);
    }
    inline void build(int p,int l,int r){
        num[p]=r-l+1;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(lson); build(rson);
    }
    #undef ls
    #undef rs
    #undef lson
    #undef rson
}T;
int a[N],ans[N];
vector<pair<int,int> >qu[N];
vector<int> G[N];
vector<tuple<int,int,int> >squ[N];
inline void ins(int sx,int ex,int sy,int ey){
    squ[sy].emplace_back(sx,ex,1);
    squ[ey+1].emplace_back(sx,ex,-1);
}
inline void dfs(int x){
    int son=0;
    for(auto t:G[x]){
        dfs(t);
        if(sub[t].size()>sub[son].size()) son=t;
    }
    swap(sub[x],sub[son]);
    for(auto t:G[x]) if(t!=son){
        for(auto v:sub[t]){
            auto iter=sub[x].upper_bound(v);
            if(v>a[x]){
                if(iter!=sub[x].end()){
                    ins(a[x]+1,v,*iter,n);
                }
                if(iter!=sub[x].begin()){
                    --iter;
                    if(*iter>a[x]){
                        ins(a[x]+1,*iter,v,n);
                    }
                }
            }else{
                if(iter!=sub[x].end()){
                    if(*iter<a[x]){
                        ins(1,v,*iter,a[x]-1);
                    }
                }
                if(iter!=sub[x].begin()){
                    iter--;
                    ins(1,*iter,v,a[x]-1);
                }
            }
        }
        for(auto v:sub[t]) sub[x].insert(v);
    }
    sub[x].insert(a[x]);   
    return ;
}
int his[N],val[N];
signed main(){
    freopen("b.in","r",stdin); freopen("b.out","w",stdout);
    n=read(); Q=read();
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=2;i<=n;++i) G[read()].emplace_back(i);       
    for(int i=1;i<=Q;++i){
        int l=read(),r=read();
        qu[r].emplace_back(l,i);
    }
    dfs(1);
    T.build(1,1,n);
    for(int i=1;i<=n;++i){
        for(auto opt:squ[i]){
            int l,r,v; tie(l,r,v)=opt;
            T.upd(l,r,v);
        }
        T.incr(1,i);
        for(auto q:qu[i]) ans[q.sec]=T.query(q.fir,i);
    }
    for(int i=1;i<=Q;++i) print(ans[i]); 
    return 0;
}

赫露艾斯塔

这个题本质上是在求一个半平面上偏序对数量

注意到直线斜率不超过 0 时可以预处理出来每个点被偏序以及偏序其它元素的次数然后做半平面上的元素求和

而对于直线斜率为正的情况,此时不构成偏序的元素是具有传递性的,所以可以进行容斥计算

对于半平面上权值和这一问题可以将每个维度分成 n 个块,并对每行的块做前缀和

对于一条直线,它横穿的块的个数不超过 n,而又在数据随机的情况下每个块里面期望 Θ(1) 个点,所以可以暴力扫

Code Display
const int D=1e4+1,N=2e5+10;
int bel[N],lef[N],rig[N];
struct Fenwick_Tree{
    int c[N];
    inline void insert(int x,int v){
        x+=D;
        for(;x<=(D<<1);x+=x&(-x)) c[x]+=v;
        return ;
    }
    inline int query(int x){
        x+=D;
        int res=0;
        for(;x;x-=x&(-x)) res+=c[x];
        return res;
    }
    inline int query(int l,int r){return query(r)-query(l-1);}
    inline void clear(){memset(c,0,sizeof(c));}
}T;
const int BL=150,Bcnt=200,Cmax=1e4;
ll A,B,C;
int n,Q;
pair<int,int> cor[N];
double k,b;
namespace spj{
    int pre[N],suf[N];
    inline void init(){
        for(int i=1;i<=n;++i){
            int j=i;
            while(j<=n&&cor[i].fir==cor[j].fir) ++j;
            --j;
            for(int k=i;k<=j;++k) pre[k]=pre[k-1]+T.query(-1e4,cor[k].sec-1);
            for(int k=i;k<=j;++k) T.insert(cor[k].sec,1);
            i=j;
        }
        T.clear();
        for(int i=n;i>=1;--i){
            int j=i;
            while(j&&cor[j].fir==cor[i].fir) --j;
            ++j;
            for(int k=i;k>=j;--k) suf[k]=suf[k+1]+T.query(cor[k].fir+1,1e4);
            for(int k=i;k>=j;--k) T.insert(cor[k].sec,1);
            i=j;
        }
        T.clear();
    }
    inline void solve(){
        if(A<0){
            int l=1,r=n,ans=0;
            while(l<=r){
                int mid=(l+r)>>1;
                if(cor[mid].fir*A+C>0) ans=mid,l=mid+1;
                else r=mid-1;
            }
            print(pre[ans]);
        }else{
            int l=1,r=n,ans=n+1;
            while(l<=r){
                int mid=(l+r)>>1;
                if(cor[mid].fir*A+C>0) ans=mid,r=mid-1;
                else l=mid+1;
            }
            print(suf[ans]);
        }
    }
}
inline int get_block(ll x){
    ll y=k*x+b;
    if(y<-Cmax) return 0;
    if(y>Cmax) return bel[Cmax+D]+1;
    return bel[y+D];
}
vector<int> node[Bcnt][Bcnt];
struct Block{
    ll sum[Bcnt][Bcnt];
    int w[N];
    inline void init(){
        for(int i=1;i<=n;++i) sum[bel[cor[i].fir+D]][bel[cor[i].sec+D]]+=w[i];
        for(int i=1;i<=bel[Cmax+D];++i){
            for(int j=bel[Cmax+D];j>=1;--j) sum[i][j]+=sum[i][j+1];
        }
        return ;
    }
}cnt;
struct Block1:Block{
    inline void prework(){
        for(int i=n;i>=1;--i){
            int j=i;
            while(cor[i].fir==cor[j].fir&&j) --j;
            ++j;
            for(int k=i;k>=j;--k) w[k]=T.query(cor[k].sec+1,Cmax);
            for(int k=i;k>=j;--k) T.insert(cor[k].sec,1);            
            i=j;
        }
        T.clear();
        init();
    }
    inline void solve(){
        ll ans=0;
        for(int i=1;i<=bel[Cmax+D];++i){
            int l=get_block(lef[i]),r=get_block(rig[i]);
            if(r<l) swap(l,r);
            ans+=sum[i][r+1];
            for(int j=l;j<=r;++j) for(auto cur:node[i][j]){
                if(cor[cur].fir*A+cor[cur].sec*B+C>0) ans+=w[cur]; 
            }
        }
        print(ans);
    }
}plain1;
struct Block2:Block{
    ll all=0;
    inline void prework(){
        for(int i=1;i<=n;++i){
            int j=i;
            while(cor[i].fir==cor[j].fir&&j<=n) ++j;
            --j;
            for(int k=i;k<=j;++k) w[k]=T.query(-Cmax,cor[k].sec-1);
            for(int k=i;k<=j;++k) T.insert(cor[k].sec,1);
            i=j;
        }
        rep(i,1,n) all+=w[i];
        T.clear();
        init();
    }
    inline void solve(){
        ll ans=all;
        for(int i=1;i<=bel[Cmax+D];++i){
            int l=get_block(lef[i]),r=get_block(rig[i]);
            if(r<l) swap(l,r);
            ans-=sum[i][r+1];
            for(int j=l;j<=r;++j) for(auto cur:node[i][j]){
                if(cor[cur].fir*A+cor[cur].sec*B+C<=0) ans-=w[cur]; 
            }
        }
        print(ans);
    }
}plain2;
struct Block3:Block{
    inline void prework(){
        for(int i=1;i<=n;++i){
            int j=i;
            while(cor[i].fir==cor[j].fir&&j<=n) ++j;
            --j;
            Down(k,j,i) w[k]=T.query(cor[k].sec,Cmax),T.insert(cor[k].sec,1);            
            i=j;
        }
        T.clear();
        init();
    }
    inline void solve(){
        ll num=0,ans=0;
        for(int i=1;i<=bel[Cmax+D];++i){
            int l=get_block(lef[i]),r=get_block(rig[i]);
            if(r<l) swap(l,r);
            ans+=sum[i][r+1];
            num+=cnt.sum[i][r+1];
            for(int j=l;j<=r;++j) for(auto cur:node[i][j]){
                if(cor[cur].fir*A+cor[cur].sec*B+C>0) ans+=w[cur],num++;
            }
        }
        print(num*(num-1)/2-ans);
    }
}plain3;
struct Block4:Block{
    ll all=0;
    inline void prework(){
        for(int i=n;i>=1;--i){
            int j=i;
            while(cor[i].fir==cor[j].fir&&j) --j;
            ++j;
            for(int k=j;k<=i;++k) w[k]=T.query(-Cmax,cor[k].sec),T.insert(cor[k].sec,1);            
            i=j;
        }
        rep(i,1,n) all+=w[i];
        T.clear();
        init();
    }
    inline void solve(){
        ll ans=all,num=n;
        for(int i=1;i<=bel[Cmax+D];++i){
            int l=get_block(lef[i]),r=get_block(rig[i]);
            if(r<l) swap(l,r);
            ans-=sum[i][r+1];
            num-=cnt.sum[i][r+1];
            for(int j=l;j<=r;++j) for(auto cur:node[i][j]){
                if(cor[cur].fir*A+cor[cur].sec*B+C<=0) num--,ans-=w[cur]; 
            }
        }
        print(num*(num-1)/2-ans);
    }
}plain4;
signed main(){
    freopen("c.in","r",stdin); freopen("c.out","w",stdout);
    for(int i=-Cmax;i<=Cmax;++i) bel[i+D]=(i+D-1)/BL+1;
    lef[1]=-Cmax;
    for(int i=-Cmax+1;i<=Cmax;++i) if(bel[i+D]!=bel[i-1+D]) lef[bel[i+D]]=i,rig[bel[i+D-1]]=i-1;
    rig[bel[Cmax+D]]=Cmax;
    n=read(); Q=read();
    for(int i=1;i<=n;++i) cor[i].fir=read(),cor[i].sec=read();
    sort(cor+1,cor+n+1);
    spj::init();
    for(int i=1;i<=n;++i){
        node[bel[cor[i].fir+D]][bel[cor[i].sec+D]].emplace_back(i);
        cnt.w[i]=1;
    }
    cnt.init();
    plain1.prework();
    plain2.prework();
    plain3.prework();
    plain4.prework();
    while(Q--){
        A=read(),B=read(),C=read();
        if(!B) spj::solve();
        else{
            k=-1.0*A/B,b=-1.0*C/B;
            if(k<0){
                if(B>0) plain1.solve();
                else plain2.solve();
            }else{
                if(B>0) plain3.solve();
                else plain4.solve(); 
            }
        }
    }
    return 0;
}

posted @   没学完四大礼包不改名  阅读(118)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示