1. 程式人生 > 其它 >[Contest on 2022.5.4] 痛啊!上週真的直接擺過去了!

[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;
}