[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)\)
要求線性求解。
\(\mathbb{S}\rm olution\)
這種題對於我這種弱雞真的太不可做了,模擬得不了分,找規律又不會,證結論更是不會……
首先其實可以發現,\((s_i,s_{i+1},t_i,t_{i+1})\) 一定有一對匹配,所以最長公共子序列的長度至少為 \(n\)。
然後給出結論 —— 勢不兩立的串一定與下面兩種之一本質相同:
-
\(s=\text{ABAB...ABA}\),\(t\) 在奇數位置均為 \(\rm C\),偶數位置可以 \(\text{A},\text{B}\) 任選。所以遇見這類題嘗試構造一種方案也不失為一種思路?考慮偶數位置有恰好 \(n\)
-
\(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\)
令人驚訝的是,所有勢不兩立的 \((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;
}