IOI 2021 集训队作业瞎做 Part 3

IH

题解暂时咕了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=10,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,m,K,cnt;
bool vis[maxn][maxn][maxn],used[maxn];
char mp[maxn][maxn][maxn];
void dfs(int x,int y,int z,int a,int b,int c,int d){
	if(x<1 || x>n || y<1 || y>m || z<1 || z>K || mp[x][y][z]!='x' || vis[x][y][z]) return;
	vis[x][y][z]=true;
	if(!used[d+4]) used[d+4]=true,cnt++;
	dfs(x+1,y,z,d,b,c,-a);
	dfs(x,y+1,z,a,d,c,-b);
	dfs(x,y,z+1,a,b,d,-c);
	dfs(x-1,y,z,-d,b,c,a);
	dfs(x,y-1,z,a,-d,c,b);
	dfs(x,y,z-1,a,b,-d,c);
} 
int main(){
	freopen("hypercube.in","r",stdin);
	freopen("hypercube.out","w",stdout); 
	K=read();m=read();n=read();
	FOR(i,1,n) FOR(j,1,m) scanf("%s",mp[i][j]+1);
	FOR(i,1,n) FOR(j,1,m) FOR(k,1,K){
		if(mp[i][j][k]=='x'){
			dfs(i,j,k,1,2,3,4);
			break;
		}
	}
	puts(cnt==8?"Yes":"No"); 
}

NG [todo]

被 pb 教育 marked。

下面以左边光照为例。

先差分变成 \(d\)。注意到若有两个相邻的草高度相等(也就是有个 \(d\)\(0\)),它们一定同时长或同时不长。所以把 \(0\) 都删掉,剩下的都是非零数。

然后会是正的一段,负的一段,再正的一段等等。一次操作,就是对于每个负段的开头 +1,接下来一个正段开头 -1。

右边同理。那么一直操作直到有一个开头或结尾变成了 0,把它删掉,搞一搞一些细节就行了。

时间复杂度看实现,大概线性到一个 log。

IJ

先考虑如果求出了一个 \(n/2\) 的字符串,怎么求答案。

翻转 \(i\)\(i+1\) 的值再做一次询问。如果是 \(0\),就说明这两个同时要改或者同时不改,否则就说明一个要改一个不改。

进行 \(n-1\) 次操作后,剩下可能的字符串只有两个(取决于第一个字符)。

这一部分总共要 \(n+1\) 次操作。

而注意到 \(n/2\) 的字符串个数是 \(O(2^n/\sqrt{n})\) 级别,随机,期望 \(O(\sqrt{n})\) 次即可出。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,w[maxn],hhh[maxn];
char str[maxn];
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int ask(){
	FOR(i,1,n) putchar(str[i]);
	puts("");
	fflush(stdout);
	int x=read();
	if(x==n) exit(0);
	return x;
}
int main(){
	n=read();
	if(n==2){
		str[1]='0';str[2]='0';
		ask();
		str[1]='0';str[2]='1';
		ask();
		str[1]='1';str[2]='0';
		ask();
		str[1]='1';str[2]='1';
		ask();
		return 0;
	}
	while(true){
		FOR(i,1,n) str[i]=rng()%2+'0';
		if(ask()==n/2) break;
	}
	FOR(i,1,n-1){
		str[i]^=1;str[i+1]^=1;
		w[i]=ask()==n/2;
		str[i]^=1;str[i+1]^=1;
	}
	FOR(i,1,n) hhh[i]=str[i]-'0';
	str[1]='0';
	FOR(i,1,n-1) str[i+1]=str[i]^w[i];
	FOR(i,1,n) str[i]^=hhh[i];
	ask();
	str[1]='1';
	FOR(i,1,n-1) str[i+1]=str[i]^w[i];
	FOR(i,1,n) str[i]^=hhh[i];
	ask();
}

FK [todo]

被 pb 教育 marked。

有点晕,暂时咕了。

KH [todo]

好家伙,你还能再水一点吗,别再挑 sb 题做了。下次一定!(

先按位。全 1 的需要有奇数个 1,不全 1 的需要有偶数个 1。

然后枚举右端点,对每一位找到上一个 0。会分成 \(O(\log a)\) 段。每一段的前缀和之类的只能是一种。瞎搞。

更没意思,不写了。

NF

\[f_{i,j}=af_{i-1,j}+bf_{i,j-1}+c\\ \]

对于每个 \(f_{1,i}(i\ne 1)\),贡献是 \(\binom{n-2+n-i}{n-2}a^{n-1}b^{n-i}\)\(f_{i,1}\) 类似(注意 \(f_{1,i}\) 并不会贡献到 \(f_{1,i+1}\),所以系数并不直接是 \((1,i)\)\((n,n)\) 的组合数)。现在看 \(c\) 的系数。

\[\sum_{i\ge 2}\sum_{j\ge 2}\binom{2n-i-j}{n-i}a^{n-i}b^{n-j} \]

不如算上 \(i<2\)\(j<2\) 的,最后减掉。只有 \(O(n)\) 个要暴力减。

\[\sum_{i}\sum_{j}\binom{2n-i-j}{n-i}a^{n-i}b^{n-j}\\ \sum_{i\le n}\sum_{j\le n}\binom{i+j}{i}a^{i}b^{j}\\ \sum_{i\le n}a^{i}\sum_{j\le n}b^{j}\binom{i+j}{i} \]

设后面那东西是 \(S_i\)

\[S_i=\sum_{j\le n}b^{j}\binom{i+j}{i}\\ bS_i=\sum_{j\le n}b^{j+1}\binom{i+j}{i}\\ bS_i=\sum_{j\le n+1}b^{j}\binom{i+j-1}{i}\\ (1-b)S_i=-b^{n+1}\binom{i+n}{i}+\sum_{j\le n}b^{j}\binom{i+j-1}{i-1}\\ (1-b)S_i=-b^{n+1}\binom{i+n}{i}+S_{i-1}\\ \]

\(S_0\) 用等比数列求和,然后递推即可。

另外还要特判 \(b=1\),此时 \(S_i\) 用一个熟知和式可得 \(\binom{i+n+1}{i+1}\)

时间复杂度 \(O(n)\)\(O(n\log n)\) 不等,取决于实现。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1000100,mod=1000003;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,a,b,c,x[maxn],y[maxn],fac[maxn],inv[maxn],invfac[maxn],s[maxn],pwa[maxn],pwb[maxn],ans;
inline int C(int n,int m){
	if(n<m || n<0 || m<0) return 0;
	return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
inline int lucas(int n,int m){
	if(n<mod) return C(n,m);
	return 1ll*lucas(n/mod,m/mod)*lucas(n%mod,m%mod)%mod;
}
inline int qpow(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
	return ans;
}
int main(){
	n=read();a=read();b=read();c=read();
	FOR(i,1,n) x[i]=read();
	FOR(i,1,n) y[i]=read();
	fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1;
	FOR(i,2,min(mod-1,2*n+1)){
		fac[i]=1ll*fac[i-1]*i%mod;
		inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
		invfac[i]=1ll*invfac[i-1]*inv[i]%mod;
	}
	pwa[0]=1;
	FOR(i,1,n) pwa[i]=1ll*pwa[i-1]*a%mod;
	pwb[0]=1;
	FOR(i,1,n+1) pwb[i]=1ll*pwb[i-1]*b%mod;
	if(b==1){
		FOR(i,0,n) s[i]=lucas(i+n+1,i+1);
	}
	else{
		int inv=qpow((1-b+mod)%mod,mod-2);
		s[0]=1ll*(1-pwb[n+1]+mod)*inv%mod;
		FOR(i,1,n) s[i]=1ll*(s[i-1]-1ll*lucas(i+n,i)*pwb[n+1]%mod+mod)*inv%mod;
	}
	FOR(i,0,n) ans=(ans+1ll*c*pwa[i]%mod*s[i])%mod; 
	FOR(i,n-1,n) FOR(j,0,n) ans=(ans-1ll*c*lucas(i+j,i)%mod*pwa[i]%mod*pwb[j]%mod+mod)%mod; 
	FOR(i,0,n-2) FOR(j,n-1,n) ans=(ans-1ll*c*lucas(i+j,i)%mod*pwa[i]%mod*pwb[j]%mod+mod)%mod;
	FOR(i,2,n) ans=(ans+1ll*x[i]*lucas(n-2+n-i,n-2)%mod*pwa[n-1]%mod*pwb[n-i])%mod;
	FOR(i,2,n) ans=(ans+1ll*y[i]*lucas(n-2+n-i,n-2)%mod*pwa[n-i]%mod*pwb[n-1])%mod;
	printf("%d\n",ans);
}

HD [todo]

wtf

对网络流一窍不通,所以咕着。

CB [todo]

稍微转换一下可以变成:\(b\) 个物品要分成 \(s\) 组,一组的代价是 \((cnt-1)sum\),其中 \(cnt\) 是个数,\(sum\) 是权值和,问最小代价。

注意到对于两个组 \(S_1,S_2\),若 \(|S_1|<|S_2|\),则 \(S_1\) 中的任意权值都比 \(S_2\) 中的任意权值大(否则交换一下)。

那么排序后,每次分出的组都是一个区间。

直接 DP 可以做到 \(O(n^3)\)。好像是决策单调性的?有点忘了。如果是的话就是 \(O(n^2\log n)\)

upd:你真的炄砒嗷,自己推的东西都能不用了嗷。

前面说过这样分出来的区间的长度一定是从小到大的(从大到小排序),所以转移的区间长度只需要枚举到剩下的长度除以剩下的段数。

由调和级数这个复杂度是 \(O(n^2\log n)\),感觉常数立马小了很多。

EI

对于正三角形,我们枚举右下角。反三角形类似。

求出每个点往左,左上,右上能延伸多少。

由当前点左,左上的长度,可以知道正三角形左下角的一个区间范围。

然后只保留左下角往右上延伸能到当前点的点。这个可以用优先队列,每次把不够的弹掉。

同时用树状数组维护区间和即可。

时间复杂度 \(O(nm\log m)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=3333,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,m,lft[maxn][maxn],lu[maxn][maxn],ru[maxn][maxn],ld[maxn][maxn],rd[maxn][maxn],b[maxn];
char s[maxn*5];
ll ans;
priority_queue<PII,vector<PII>,greater<PII> > pq;
inline void update(int p,int v){
	for(int i=p;i<=(m+1)>>1;i+=i&-i) b[i]+=v;
}
inline int query(int p){
	int s=0;
	for(int i=p;i;i-=i&-i) s+=b[i];
	return s;
}
inline int query(int l,int r){
	if(l>r) return 0;
	return query(r)-query(l-1);
}
int main(){
	n=read();m=read();
	FOR(i,1,2*n-1){
		while(!strlen(s+1)) fgets(s+1,maxn*5-1,stdin);
		if(i%2==1){
			FOR(j,1,2*m-1) if(s[j]=='x' && s[j-1]=='-'){
				int ii=(i+1)/2,jj=(j+3)/4;
				lft[ii][jj]=lft[ii][jj-1]+1;
			}
		}
		else{
			FOR(j,1,2*m-1) if(s[j]=='/'){
				int ii=i/2+1,jj=(j+2)/4;
				ru[ii][jj]=ru[ii-1][jj+(ii-1)%2]+1;
			}
			else if(s[j]=='\\'){
				int ii=i/2+1,jj=j/4+1;
				lu[ii][jj]=lu[ii-1][jj-ii%2]+1;
			}
		}
		FOR(i,1,2*m-1) s[i]='\0';
	}
	FOR(i,1,n){
		while(!pq.empty()) pq.pop();
		FOR(j,1,(m+i%2)/2){
			while(!pq.empty() && pq.top().first<j) update(pq.top().second,-1),pq.pop();
			int l=j-min(lft[i][j],lu[i][j]),r=j-1;
			ans+=query(l,r);
			pq.push(MP(ru[i][j]+j,j));
			update(j,1);
		}
		FOR(j,1,(m+1)/2) b[j]=0;
	}
	ROF(i,n-1,1) FOR(j,1,(m+i%2)/2){
		if(ru[i][j]+1==ru[i+1][j-i%2]) ld[i][j]=ld[i+1][j-i%2]+1;
		if(lu[i][j]+1==lu[i+1][j+(i-1)%2]) rd[i][j]=rd[i+1][j+(i-1)%2]+1;
	}
	ROF(i,n,1){
		while(!pq.empty()) pq.pop();
		FOR(j,1,(m+i%2)/2){
			while(!pq.empty() && pq.top().first<j) update(pq.top().second,-1),pq.pop();
			int l=j-min(lft[i][j],ld[i][j]),r=j-1;
			ans+=query(l,r);
			pq.push(MP(rd[i][j]+j,j));
			update(j,1);
		}
		FOR(j,1,(m+1)/2) b[j]=0;
	}
	printf("%lld\n",ans);
}

DD

\(n\)\((a,b)\)\(m\)\((c,d)\),各选出一个满足 \(a\le c,b\le d\),求 \((c-a)(d-b)\) 最大。

肯定先排序。

注意到若 \((a_1,b_1),(a_2,b_2)\) 满足 \(a_1<a_2,b_1<b_2\),那么 \((a_2,b_2)\) 没用,所以最后只会保留 \(a\) 递增,\(b\) 递减的对。我们从后往前做单调栈即可(即保留等于前缀最大值的那些对)。

\((c,d)\) 同理,保留 \(c\) 递增,\(d\) 递减的对(这里我们要正着单调栈,即保留等于后者最大值的那些对,原因在下面)。

考虑 \((a_1,b_1),(a_2,b_2),(c_1,d_1),(c_2,d_2)\),若 \(a_1<a_2,c_1<c_2\)\((a_1,b_1)\) 的最优配对是 \((c_2,d_2)\),那么 \((a_2,b_2)\) 的最优配对肯定不是 \((c_1,d_1)\)

因为 \((c_2-a_1)(d_2-b_1)>(c_1-a_1)(d_1-b_1)\)

\(c_2d_2-a_1d_2-b_1c_2>c_1d_1-a_1d_1-b_1c_1\)

那么若 \((c_2-a_2)(d_2-b_2)<(c_1-a_2)(d_1-b_2)\)

\(c_2d_2-a_2d_2-b_2c_2<c_1d_1-a_2d_1-b_2c_1\)

相加得 \((c_2d_2-a_1d_2-b_1c_2)+(c_1d_1-a_2d_1-b_2c_1)>(c_1d_1-a_1d_1-b_1c_1)+(c_2d_2-a_2d_2-b_2c_2)\)

\((a_1-a_2)(d_1-d_2)>(b_2-b_1)(c_1-c_2)\)

左边是负数,右边是正数,矛盾。

所以满足决策单调性,分治即可。

注意枚举 \((a,b)\) 时只能计算 \((c,d)\)\(a<c\) 的对,否则会负负得正出问题。

注意到虽然决策有了偏序的限制,但是仍然满足决策单调性(前面的决策完全包含后面的决策,且可以直接套用上面的证明过程来排除决策)。

而因为这种偏序限制,导致如果我们对于 \((c,d)\) 也倒着做单调栈,会排除那些看起来不优,实际上比它更优的都不合法的决策。而正着做,就能保证排除它的决策都在它后面(也就是如果这个决策合法,排除它的决策也合法)。

但是我当时蠢了,没想到这样,直接把 \((c,d)\) 单调栈的部分去掉也过了,我不会证明 /kk

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=500050,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
struct wtf{
	int x,y;
	bool operator<(const wtf &w)const{
		if(x!=w.x) return x<w.x;
		return y<w.y;
	}
}a[maxn],b[maxn],A[maxn],B[maxn];
int n,m,stk[maxn],tp; 
ll ans;
void solve(int l,int r,int L,int R){
	if(l>r) return;
	int mid=(l+r)>>1,p=0;
	ll hhh=-1e18;
	FOR(i,L,R){
		ll tmp=1ll*(B[i].x-A[mid].x)*(B[i].y-A[mid].y);
		if(B[i].x>=A[mid].x && tmp>hhh) p=i,hhh=tmp;
	}
	ans=max(ans,hhh);
	solve(l,mid-1,L,p);
	solve(mid+1,r,p,R);
}
int main(){
	n=read();m=read();
	FOR(i,1,n) a[i].x=read(),a[i].y=read();
	FOR(i,1,m) b[i].x=read(),b[i].y=read();
	sort(a+1,a+n+1);
	ROF(i,n,1){
		while(tp && a[stk[tp]].y>=a[i].y) tp--;
		stk[++tp]=i;
	}
	FOR(i,1,tp) A[tp-i+1]=a[stk[i]];
	n=tp;
	tp=0;
	sort(b+1,b+m+1);
	FOR(i,1,m){
		while(tp && b[stk[tp]].y<=b[i].y) tp--;
		stk[++tp]=i;
	}
	FOR(i,1,tp) B[i]=b[stk[i]];
	m=tp;
	while(n && A[n].x>B[m].x) n--;
	if(!n) return puts("0"),0; 
	solve(1,n,1,m);
	printf("%lld\n",ans);
}

MJ [todo]

可以把所有 \((u,v),(v,u)\) 放到一组,不同的放到不同组,然后分类讨论。

稍微操作一下就可以变成:给定一个 01 序列,要 \(a\) 的代价删掉一个 0,\(b\) 的代价删掉一个 \(1\)\(c\) 的代价删掉前面一个 0 后面一个 1,\(d\) 的代价删掉前面一个 1 后面一个 0。问最小代价。

原来不可行的操作,可以看成代价 INF。然后进行一些操作,使得 \(a\le c,c\le a+b\) 等等。

先枚举后两种操作下在了哪,剩下的再用前两种操作捡漏。

以 01 为例。若操作的 0 前面有未被操作的 0,显然放到前面更优;1 也同样。所以应该是最前一些 0 和最后一些 1 属于操作 01。10 类似。

分别枚举两种操作的次数 \(i,j\),再整理一下,会发现对 \(i,j,i+j\) 分别有个上界限制,最后总代价是 \(k_1i+k_2j+b\),其中 \(k_1,k_2\) 都是负数。

那么应该尽可能取 \(k\) 小的那个,再取另外一个。

时间复杂度就那样。

感觉有点虚,但又很烦,不太想弄。

QC

这题令我非常生气。

我再菜也没必要在集训队作业里放个入门题来嘲讽我吧???

还有脸说,明明确实被嘲讽了,居然还能调这么久

就是暴露我在集训队作业里找入门题的摸鱼行为,居然还能摸不动

先记录每对辅音有多少相邻的。

辅音只有 19 个,枚举大小写,搜就完了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1000100,mod=998244353;
const char vowel[]="aeiouwy";
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,cnt[22][22],ans,asum=-1;
char s[maxn];
inline int id(char c){
	int hhh=c-'a';
	FOR(i,0,6){
		if(c==vowel[i]) return -1;
		else if(c>vowel[i]) hhh--;
	}
	return hhh;
}
void dfs(int dep,int S,int sum){
	if(dep>=19){
		if(sum>asum) asum=sum,ans=S;
		return;
	}
	int tmp=sum;
	FOR(i,0,dep-1) if((S>>i)&1) sum+=cnt[i][dep];
	dfs(dep+1,S,sum);
	sum=tmp;
	FOR(i,0,dep-1) if(!((S>>i)&1)) sum+=cnt[i][dep];
	dfs(dep+1,S|(1<<dep),sum);
}
int main(){
	freopen("consonant.in","r",stdin);
	freopen("consonant.out","w",stdout); 
	scanf("%s",s+1);
	n=strlen(s+1);
	FOR(i,1,n-1){
		int x=id(s[i]),y=id(s[i+1]);
		if(x!=-1) cnt[x][y]++,cnt[y][x]++;
	}
	dfs(0,0,0);
	FOR(i,1,n){
		int x=id(s[i]);
		if(x!=-1 && (ans>>x)&1) printf("%c",s[i]-'a'+'A');
		else printf("%c",s[i]);
	}
}

LE [fake]

如果一个点有超过两个儿子,肯定无解。

先考虑一条链,枚举在哪往下转,然后有三种情况,分别都能前缀和或更高超的技巧来 \(O(n)\) 算。

如果根有两个儿子,瞎讨论没了。

如果根有一个儿子,瞎讨论没了。

没了个鬼,还有。

HB [todo]

感觉很久以前模拟赛考过,当时自闭,现在看来好像很蠢。

每个字符串里至多一个问号,看起来就很 2-SAT。

把每个字符串变成的两个串都扔进 Trie,然后一个点往子树瞎连连。

感觉甚至不需要线段树优化建图,时间复杂度 \(O(\sum|s_i|)\)

感觉挺对,挂了再说。

posted @ 2020-10-15 20:56  ATS_nantf  阅读(108)  评论(0编辑  收藏  举报