T1:词典
有一个好猜的结论:对于一个字符串,若它当中的最小字符大于等于某其他字符串中的最大字符,那么这个字符串一定不可行。
证明也很简单,若最小字符大于最大字符,显然一定不可行。若最小字符等于最大字符,由于字符串长度相同,且字符串两两不同,所以次小的一定大于另一个次大的,依然不可行,由此得证。代码及其简单,就不贴了。
T2:三值逻辑
见到这个题第一眼就可以想到并查集了。对于 true 和 false,我们可以将它们视为正负关系,这样就极大简化操作了。初始化常量,令 T = − F , U = 0 T=-F,U=0 T=−F,U=0,然后我们处理完所有赋值操作后,再处理答案。
考虑一个数 x x x,在并查集中,若 x x x 祖先为 U U U,那么答案自然加 1 1 1;若它的祖先有 − x -x −x,说明是非关系起冲突了,那么 x x x 只能为 U U U。由此,我们处理的重点便是如何判断 x x x 是否存在这两个祖先。
若 x = T / F / U x=T/F/U x=T/F/U,可以直接判断;若之前访问过 − x -x −x,那么也可以判断。然后考虑 x x x 大于或小于 0 0 0 的情况,注意处理数组越界的情况,就可以了。代码不长,但细节还是比较多的。
#include<bits/stdc++.h>
using namespace std;
#define rd read()
const int N=3e5+10,T=2e5+10,F=-T,U=0;
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
int c,t,n,m,fa[N],vis[N];
int find(int x){
if(x==T||x==F) return x;
if(x==U||vis[-x+n]) return U;
if(vis[x+n]) return T;
int res;
if(x<0){
if(x==-fa[-x]) return x;//为根节点
vis[x+n]=1,res=find(-fa[-x]),vis[x+n]=0;
}
else{
if(x==fa[x]) return x;
vis[x+n]=1,res=fa[x]=find(fa[x]),vis[x+n]=0;
}
return res;
}
int main(){
c=rd,t=rd;
while(t--){
n=rd,m=rd;memset(fa,0,sizeof(fa)),memset(vis,0,sizeof(is));
for(int i=1;i<=n;i++) fa[i]=i;
while(m--){
char op;cin>>op;int x=rd,y;
if(op=='T') fa[x]=T;
else if(op=='F') fa[x]=F;
else if(op=='U') fa[x]=U;
else if(op=='+') y=rd,fa[x]=fa[y];
else y=rd,fa[x]=-fa[y];
}
int ans=0;for(int i=1;i<=n;i++){
//cout<<find(i)<<":::"<<endl;
if(find(i)==U) ans++;
}
printf("%d\n",ans);
}
return 0;
}
T3:双序列拓展
感觉比 T4 难。
神仙思维题,依然是由特殊性质启发正解的题。
我们先看两个序列满足的条件,实际上就是对于任意位置 i i i,要么 f i > g i f_i>g_i fi>gi,要么 f i < g i f_i<g_i fi<gi,且这种大小关系只能满足一个。我们可以规定 f f f 恒小于 g g g,两种情况处理方法几乎是一样的。
先看 Subtask 1~7,可以用比较暴力的 O ( n 2 ) O(n^2) O(n2