• [Contest on 2022.5.4] 痛啊!上周真的直接摆过去了!


    \(\cal T_1\) 路人女主

    \(\mathbb{D}\rm escription\)

    称一个长度为 \(2n+1\),字符集为 \(\{\text{A},\text{B},\text{C}\}\) 为好的当且仅当任意相邻两个字符不同。称两个长度为 \(2n+1\) 的好字符串对 \((s,t)\) 为势不两立的当且仅当它们的最长公共子序列长度恰为 \(n\)

    给定两个包含 \(\text{A},\text{B},\text{C},?\) 的字符串 \(s,t\),求有多少种把 \(?\) 替换成 \(\text{A},\text{B},\text{C}\) 的方案,使得 \((s,t)\) 是势不两立的,对 \(998244353\) 取模。

    要求线性求解。

    \(\mathbb{S}\rm olution\)

    这种题对于我这种弱鸡真的太不可做了,模拟得不了分,找规律又不会,证结论更是不会……

    首先其实可以发现,\((s_i,s_{i+1},t_i,t_{i+1})\) 一定有一对匹配,所以最长公共子序列的长度至少为 \(n\)

    然后给出结论 —— 势不两立的串一定与下面两种之一本质相同:

    1. \(s=\text{ABAB...ABA}\)\(t\) 在奇数位置均为 \(\rm C\),偶数位置可以 \(\text{A},\text{B}\) 任选。所以遇见这类题尝试构造一种方案也不失为一种思路?考虑偶数位置有恰好 \(n\) 个,所以不妨假设将 \(t\) 的偶数位全都匹配,就得到了这组非常优美的构造(我在说什么;
    2. \(s,t\) 在偶数位置均为 \(\rm C\),并且存在一个 \(k\),使得 \(s\) 的前 \(k\) 个奇数位置为 \(\rm A\),后 \(n+1-k\) 个奇数位置为 \(\rm B\)\(t\) 则相反。这种情况可以归纳证明是势不两立的:首先可以发现最长公共子序列只有可能包含 \(\{\text{C}\}\)\(\{\text{A,C}\}\)\(\{\text{B,C}\}\)。容易发现后面两种其实是等价的,而第一种的最长公共子序列显然为 \(n\),所以我们只考虑第二种情况。另外还可以发现,\(k>n+1-k\) 的情况严格包含于 \(k\le n+1-k\) 之中,所以我们只用考虑后者。当 \(k<n+1-k\) 时(没有将符号打错),\(s\) 中的 \(\text{ACAC...AC}\) 一定能被 \(t\) 中的 \(\text{CACA...CA}\) 全部匹配完,总长为 \(2k\),在这之后,如果 \(t\) 中的 \(\text{CACA...CA}\) 还有富余,可以再匹配一段 \(\rm C\),长度为 \((n+1-k)-k-1=n-2k\),所以最长公共子序列长度为 \(n\)。不过我们还没有考虑开头匹配了 \(\rm C\) 的情况,事实上可以发现,每当开头匹配一个 \(\rm C\),就意味着 至少 少匹配一个 \(\rm A\)(画画图可以知道这是一个类似势能的玩意),所以不可能更优。当 \(k=n+1-k\) 时的证明思路也大体相似,只是结论可能有些许不同,这里不再赘述。

    令人惊讶的是,所有势不两立的 \((s,t)\) 都包括于上述两类。所以直接按这两种类型计数即可。

    证明?一是因为证明很长很烦,二是因为它好像有点问题,所以就咕了。

    $\mathbb{C}\rm ode $

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
        T x=0; char s; bool f=0;
        while(!isdigit(s=getchar())) f|=(s=='-');
        for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
        return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
        static int writ[50], w_tp=0;
        if(x<0) putchar('-'), x=-x;
        do writ[++w_tp] = x-x/10*10, x/=10; while(x);
        while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    const int maxn = 1e6+5;
    const int mod = 998244353;
    
    char s[maxn<<1],t[maxn<<1];
    int n,m,pw[maxn];
    long long ans;
    
    inline bool eq(const char& a,const char& b) {
    	return a=='?' || a==b;
    }
    
    inline void check1(char* s,char* t,const char& a,const char& b,const char& c) {
    	for(int i=1;i<=m;i+=2) if(!eq(s[i],a) || !eq(t[i],c)) return;
    	int cnt=0;
    	for(int i=2;i<=m;i+=2) {
    		if(!eq(s[i],b) || t[i]==c) return;
    		cnt += t[i]=='?';
    	}
    	ans = (ans+pw[cnt])%mod;
    }
    
    inline void check2(const char& a,const char& b,const char& c) {
    	for(int i=2;i<=m;i+=2) if(!eq(s[i],c) || !eq(t[i],c)) return;
    	int cnt=0;
    	for(int i=1;i<=m;i+=2) cnt += eq(s[i],a)+eq(t[i],b);
    	ans -= (cnt==2*(n+1));
    	for(int i=1;i<m;i+=2)
    		cnt += eq(s[i],b)-eq(s[i],a) + eq(t[i],a)-eq(t[i],b),
    		ans += (cnt==2*(n+1));
    }
    
    void calc(const char& a,const char& b,const char& c) {
    	check1(s,t,a,b,c); check1(t,s,a,b,c);
    	check2(a,b,c);
    }
    
    void goto_solve_it() {
    	scanf("%d %s %s",&n,s+1,t+1);
    	m=n*2+1; ans=0;
    	for(int i='A'; i<='C'; ++i)
    		for(int j='A'; j<='C'; ++j)
    			for(int k='A'; k<='C'; ++k)
    				if(i!=j && j!=k && i!=k) calc(i,j,k);
    	print((ans%mod+mod)%mod,'\n');
    }
    
    int main() {
    	freopen("a.in","r",stdin);
    	freopen("a.out","w",stdout);
    	for(int i=(pw[0]=1);i<=maxn-5;++i)
    		pw[i] = 2ll*pw[i-1]%mod;
    	for(int T=read(9); T; --T)
    		goto_solve_it();
    	return 0;
    }
    

    \(\cal T_2\) 铃原露露

    \(\mathbb{D}\rm escription\)

    给定一棵根为 \(1\) 的有根树,\(\{a\}\) 是一个排列。共 \(q\) 次询问,每次询问给出 \((l,r)\),询问有多少个二元组 \(L,R\),满足 \(l\le L\le R\le r\),且对任意 \(L\le a_x\le a_y\le R\),有 \(x,y\) 在树上的最近公共祖先 \(z\) 满足 \(L\le a_z\le R\)

    \(1\le n,m\le 2\cdot 10^5\).

    \(\mathbb{S}\rm olution\)

    历史最值线段树,但是现在只会 \(50\) 分,等复习到了再回来做(

    $\mathbb{C}\rm ode $

    
    

    \(\cal T_3\) 赫露艾斯塔

    \(\mathbb{D}\rm escription\)

    给定 \(n\) 个互不相同的点 \((x_i,y_i)\),共 \(m\) 次询问,每次询问给出 \(A,B,C\),问满足 \(x_i<x_j,y_i<y_j,Ax_i+By_i+C>0,Ax_j+By_j+C>0\) 的二元组 \((x_i,y_i)\) 的个数。

    \(A^2+B^2>0,|A|,|B|,|C|\le 10^8,1\le n,m\le 2\cdot 10^5,|x_i|,|y_i|\le 10^4\),其中 \(x_i,y_i\) 保证随机。

    \(\mathbb{S}\rm olution\)

    \(\bold{Warning}\):这题不会,现在只会可怜的 \(A=0\) 部分分。


    如果 \(A=0\),显然可以将 \(Ax_i+By_i+C>0\) 的条件转化成 \(y_i>-\frac{C}{B}\) 或者 \(y_i<-\frac{C}{B}\),你会发现符合条件的点正好是经 \(y\) 坐标排序后的一段前/后缀,所以可以预处理一段前/后缀的答案,询问时二分即可。

    但是我不是这样想的,我当时一看,诶嘛,这不是三维偏序吗?可怜的我根本没有意识到所谓的第三维根本没有施加在点对上,所以就写了个非常伞兵的 \(\rm cdq\),还是两只 \(\log\) 的……

    $\mathbb{C}\rm ode $

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
        T x=0; char s; bool f=0;
        while(!isdigit(s=getchar())) f|=(s=='-');
        for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
        return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
        static int writ[50], w_tp=0;
        if(x<0) putchar('-'), x=-x;
        do writ[++w_tp] = x-x/10*10, x/=10; while(x);
        while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <iostream>
    # include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 2e5+5;
    
    ll ans;
    int n,m,val[maxn],rag;
    struct node {
        int x,y;
        bool operator < (const node& t) { return y<t.y; }
    } s[maxn];
    
    namespace FwTree {
    
    int c[maxn];
    
    inline int lowbit(int x) { return x&-x; }
    inline int ask(int x,int ret=0) {
        for(; x; x-=lowbit(x)) ret += c[x];
        return ret;
    }
    inline void add(int x,int k) {
        for(; x<=rag; x+=lowbit(x)) c[x] += k;
    }
    
    }
    
    namespace Subtask_1 {
    
    node Node[1005];
    
    void work() {
        int len;
        while(m --) {
            ll a=read(9), b=read(9), C=read(9); 
            ans=0; int fro=1; len=0;
            for(int i=1;i<=n;++i) {
                FwTree::c[i]=0;
                if(a*s[i].x+b*s[i].y+C>0) 
                    Node[++len] = (node){
                        int(lower_bound(val+1,val+rag+1,s[i].x)-val),
                        s[i].y
                    };
            }
            sort(Node+1,Node+len+1);
            for(int i=1; i<=len; ++i)
                if(Node[i].y!=Node[fro].y) {
                    for(int j=fro; j<i; ++j)
                        FwTree::add(Node[j].x,1);
                    fro = i; ans += FwTree::ask(Node[i].x-1);
                } else ans += FwTree::ask(Node[i].x-1);
            print(ans,'\n');
        }
    }
    
    }
    
    namespace Subtask_2 {
    
    bool option;
    ll res[maxn],ret[maxn];
    struct Node { int opt,p; double r; };
    Node seq[2][maxn<<1];
    
    void dicon(Node* f,int l,int r) { 
        if(l>=r) return;
        int mid = l+r>>1; dicon(f,l,mid);
        sort(f+mid+1,f+r+1,[](const Node& x,const Node& y) {
            return x.r<y.r;
        });
        sort(f+l,f+mid+1,[](const Node& x,const Node& y) {
            return x.r<y.r;
        });
        ll all=0; int pos=l;
        for(int i=l;i<=mid;++i) all += ret[f[i].opt];
        for(int i=mid+1;i<=r;++i) 
            if(f[i].opt) {
                while(pos<=mid && f[pos].r<f[i].r) {
                    if(f[pos].opt) FwTree::add(f[pos].p,1);
                    ++ pos;
                }
                ret[f[i].opt] += FwTree::ask(f[i].p-1);
            } else res[f[i].p] += all;
        for(int i=l;i<pos;++i) if(f[i].opt) FwTree::add(f[i].p,-1);
        pos = mid;
        for(int i=r;i>mid;--i)
            if(f[i].opt) {
                while(pos>=l && f[pos].r>f[i].r) {
                    if(f[pos].opt) FwTree::add(rag-f[pos].p+1,1);
                    -- pos;
                }
                ret[f[i].opt] += FwTree::ask(rag-f[i].p);
            }
        for(int i=mid;i>pos;--i) if(f[i].opt) FwTree::add(rag-f[i].p+1,-1);
        if(option)
            sort(f+mid+1,f+r+1,[](const Node& x,const Node& y) {
                return (x.r!=y.r)? x.r<y.r: (x.opt^y.opt)? x.opt<y.opt: x.p<y.p;
            }); 
        else 
            sort(f+mid+1,f+r+1,[](const Node& x,const Node& y) {
                return (x.r!=y.r)? x.r>y.r: (x.opt^y.opt)? x.opt<y.opt: x.p<y.p;
            }); 
        dicon(f,mid+1,r);
    }
    
    void work() {
        int m1=0, m2=0;
        for(int i=1;i<=n;++i) {
            s[i].x = lower_bound(val+1,val+rag+1,s[i].x)-val;
            seq[0][++m1] = (Node){i,s[i].x,0.0+s[i].y};
            seq[1][++m2] = (Node){i,s[i].x,0.0+s[i].y};
        }
        for(int i=1;i<=m;++i) {
            int a=read(9), b=read(9), c=read(9);
            if(b>0) seq[0][++m1] = (Node){0,i,-1.0*c/b};
            else seq[1][++m2] = (Node){0,i,-1.0*c/b};
        }
        sort(seq[0]+1,seq[0]+m1+1,[](const Node& x,const Node& y) {
            return (x.r!=y.r)? x.r>y.r: (x.opt^y.opt)? x.opt<y.opt: x.p<y.p;
        });
        sort(seq[1]+1,seq[1]+m2+1,[](const Node& x,const Node& y) {
            return (x.r!=y.r)? x.r<y.r: (x.opt^y.opt)? x.opt<y.opt: x.p<y.p;
        });
        if(m1>n) dicon(seq[0],1,m1); option=true;
        for(int i=1;i<=n;++i) ret[i]=0; 
        if(m2>n) dicon(seq[1],1,m2);
        for(int i=1;i<=m;++i) print(res[i],'\n');
    }
    
    }
    
    int main() {
    	freopen("c.in","r",stdin);
    	freopen("c.out","w",stdout);
    	n=read(9), m=read(9);
        for(int i=1;i<=n;++i)
            val[i]=s[i].x=read(9), s[i].y=read(9);
        sort(val+1,val+n+1); rag = unique(val+1,val+n+1)-val-1;
        if(n<=1000 && m<=1000) return Subtask_1::work(), (0-0);
        Subtask_2::work();
    	return 0;
    }
    
  • 相关阅读:
    shell数组(产生不同的随机数)
    统计服务连接状况
    子网掩码与子网划分
    oracle 12g sqlplus安装
    MySQL的备份和还原
    mysql日志
    mysql用户和权限管理
    mysql show
    CentOS Linux解决Device eth0 does not seem to be present
    mysqldump命令详解(转载)
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/16244963.html
Copyright © 2020-2023  润新知