好题 np.

对于20分 显然可以爆搜。

对于50分 可以发现每个字符串上的问号要么是0,要么是1.考虑枚举一个字符串当前是0还是1 这会和其他字符串产生矛盾。

所以容易 发现这是一个2-sat问题。

拆点 把任意两个产生矛盾的字符串进行连边。然后最后判矛盾即可。

n^2枚举 建图 判断矛盾时使用字符串hash 要分类讨论4种情况。

using namespace std;
const int MAXN=1010,maxn=500010,cc1=19260817,cc2=114514;
int n,mark,cnt,top,id,len;
string a[MAXN];
int flag[MAXN],c[MAXN<<1],low[MAXN<<1],dfn[MAXN<<1],s[MAXN<<1];
int lin[MAXN<<1],ver[MAXN*MAXN<<2],nex[MAXN*MAXN<<2],w[MAXN];
vector<int>h0[MAXN];//表示为0时的前缀hash值 或者表示什么都不是的前缀hash值.
vector<int>h1[MAXN];//表示为1时的前缀hash值
struct wy
{
int len,id;
}t[MAXN];
inline int cmp(wy a,wy b){return a.len<b.len;}
inline void dfs(int x)
{
low[x]=dfn[x]=++cnt;
s[++top]=x;
go(x)
{
if(!dfn[tn])
{
dfs(tn);
low[x]=min(low[x],low[tn]);
}
else if(!c[tn])low[x]=min(low[x],dfn[tn]);
}
if(dfn[x]==low[x])
{
int y=0;++id;
while(y!=x)
{
y=s[top--];
c[y]=id;
}
}
}
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
int main()
{
freopen("code.in","r",stdin);
freopen("code.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;
if(n<=1000)
{
rep(1,n,i)
{
cin>>a[i];
t[i]=(wy){a[i].size(),i};
}
rep(1,n,i)
{
ll w0=0,w1=0;
rep(0,((int)a[i].size())-1,j)
{
w0=w0*P%mod;
w1=w1*P%mod;
if(a[i][j]=='?')
{
flag[i]=j+1;
w0=(w0+cc1)%mod;
w1=(w1+cc2)%mod;
}
else
{
if(a[i][j]=='0')w1=(w1+cc1)%mod,w0=(w0+cc1)%mod;
else w1=(w1+cc2)%mod,w0=(w0+cc2)%mod;
}
h0[i].pb(w0);
h1[i].pb(w1);
}
}
//x表示这个点选择0 x+n表示这个点选择1.
sort(t+1,t+1+n,cmp);
rep(1,n,i)
{
int x=t[i].id;//x.len<=y.len
int xx=t[i].len;
rep(i+1,n,j)
{
int y=t[j].id;
if(!flag[x]&&(!flag[y]||flag[y]>xx))
{
if(h0[x][xx-1]==h0[y][xx-1])
{
puts("NO");
return 0;
}
continue;
}
if(!flag[x]&&flag[y]<=xx)
{
if(h0[x][xx-1]==h0[y][xx-1])add(y,y+n);
if(h0[x][xx-1]==h1[y][xx-1])add(y+n,y);
continue;
}
if(flag[x]&&(!flag[y]||flag[y]>xx))
{
if(h0[x][xx-1]==h0[y][xx-1])add(x,x+n);
if(h1[x][xx-1]==h0[y][xx-1])add(x+n,x);
}
if(flag[x]&&flag[y]<=xx)
{
if(h0[x][xx-1]==h0[y][xx-1])add(x,y+n),add(y,x+n);
if(h0[x][xx-1]==h1[y][xx-1])add(x,y),add(y+n,x+n);
if(h1[x][xx-1]==h0[y][xx-1])add(x+n,y+n),add(y,x);
if(h1[x][xx-1]==h1[y][xx-1])add(x+n,y),add(y+n,x);
}
}
}
rep(1,n+n,i)if(!dfn[i])dfs(i);
rep(1,n,i)if(c[i]==c[i+n]){puts("NO");return 0;}
puts("YES");
}
return 0;
}

考虑100分。

我考试的时候想了一波trie树 但是当时思考没有在这个暴力的基础上思考 所以 建图也很麻烦 所以弃疗了。

可以发现我们拆过点后 把这些串给放到trie树上。

可以发现连边的时候 使用前缀和连边 即可优化建图了。

对于某个节点存放多个节点 这个时候 对这个节点内部再进行一次前缀和优化建图。

对于某个节点不存在? 考虑子树内和链上上的节点都需要自己向自己的对立连边 这个使用懒标记即可。

上传标记和 标记的时候注意判断不合法的情况。

这个优化建图还是很精髓的。充分的利用了trie树的性质。

注意空间不要开小了 计算不了点数可以开到空间上界小一点。

const int MAXN=500010*3;
int n,sum,cnt=1,top,id,len,last=1,mark;
string a[MAXN];
int t[MAXN][2],ne[MAXN],pos[MAXN];
int flag[MAXN],c[MAXN<<3],low[MAXN<<3],dfn[MAXN<<3],s[MAXN<<3],w1[MAXN],w2[MAXN];
int lin[MAXN<<3],ver[MAXN<<3],nex[MAXN<<3],add1[MAXN],add2[MAXN];
vector<int>g[MAXN];
inline void dfs(int x)
{
low[x]=dfn[x]=++cnt;
s[++top]=x;
go(x)
{
if(!dfn[tn])
{
dfs(tn);
low[x]=min(low[x],low[tn]);
}
else if(!c[tn])low[x]=min(low[x],dfn[tn]);
}
if(dfn[x]==low[x])
{
int y=0;++id;
while(y!=x)
{
y=s[top--];
c[y]=id;
}
}
}
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void insert(int x,int op)
{
int p=1,c=min(x,ne[x]);
for(ui i=0;i<a[c].size();++i)
{
int w=a[c][i]-'0';
if(!t[p][w])t[p][w]=++cnt;
p=t[p][w];
}
if(op)g[p].pb(x);
else
{
if(add1[p])mark=1;
add1[p]=1,add2[p]=1;
}
pos[x]=p;
}
inline void dfs(int x,int fa)
{
int s1=++sum,s2=++sum;
if(w1[fa])add(s1,w1[fa]);//w1[x]表示当前点向前缀所有的点的相反点的连边
if(w2[fa])add(w2[fa],s2);//w2[x]表示前缀所有的点向当前点的连边.
int c1=++sum,c2=++sum,cc1,cc2;//c1表示当前这个点对前缀和的相反点的连边.
//c2表示前缀和的所有点对当前点的连边.
for(ui i=0;i<g[x].size();++i)
{
int tn=g[x][i];
//cout<<tn<<endl;
add(tn,c1);
cc1=++sum;
add(cc1,c1);
add(cc1,ne[tn]);
c1=cc1;
add(c2,ne[tn]);
cc2=++sum;
add(c2,cc2);
add(tn,cc2);
c2=cc2;
if(w1[fa])add(tn,w1[fa]);
if(w2[fa])add(w2[fa],ne[tn]);
add(s1,ne[tn]);
add(tn,s2);
}
//puts("ww");
w1[x]=s1;w2[x]=s2;
if(t[x][0])add1[t[x][0]]|=add1[x],dfs(t[x][0],x);//传递子树标记
if(t[x][1])add1[t[x][1]]|=add1[x],dfs(t[x][1],x);
if(add2[x]&&add2[t[x][0]])mark=1;
if(add2[x]&&add2[t[x][1]])mark=1;
add2[x]|=add2[t[x][0]];//传递链上标记.
add2[x]|=add2[t[x][1]];
}
int main()
{
freopen("code.in","r",stdin);
freopen("code.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;//cout<<1<<endl;
rep(1,n,i)cin>>a[i];
rep(1,n,i)
{
ne[i]=i+n;ne[i+n]=i;
for(ui j=0;j<a[i].size();++j)if(a[i][j]=='?'){flag[i]=j+1;break;}
if(flag[i])
{
a[i][flag[i]-1]='0';
insert(i,1);
a[i][flag[i]-1]='1';
insert(i+n,1);
}
else insert(i,0);
}
sum=2*n;dfs(1,0);
if(mark){puts("NO");return 0;}
rep(1,2*n,i)if(add1[pos[i]]||add2[pos[i]])if(pos[i])add(i,ne[i]);
rep(1,2*n,i)if(!dfn[i])dfs(i);
rep(1,n,i)if(c[i]==c[i+n]){puts("NO");return 0;}
puts("YES");
return 0;
}

4.15 省选模拟赛 编码 trie树 前缀和优化建图 2-sat的更多相关文章

  1. 5.15 省选模拟赛 容斥 生成函数 dp

    LINK:5.15 T2 个人感觉生成函数更无脑 容斥也好推的样子. 容易想到每次放数和数字的集合无关 所以得到一个dp f[i][j]表示前i个数字 逆序对为j的方案数. 容易得到转移 使用前缀和优 ...

  2. 6.15 省选模拟赛 老魔杖 博弈论 SG函数

    这道题确实没有一个很好的解决办法 唯一的正解可能就是打表找规律 或者 直接猜结论了吧. 尽管如此 在此也给最终结论一个完整的证明. 对于70分 容易发现状态数量不大 可以进行暴力dp求SG函数. 原本 ...

  3. 5.15 省选模拟赛 T1 点分治 FFT

    LINK:5.15 T1 对于60分的暴力 都很水 就不一一赘述了. 由于是询问所有点的这种信息 确实不太会. 想了一下 如果只是询问子树内的话 dsu on tree还是可以做的. 可以自己思考一下 ...

  4. 5.4 省选模拟赛 修改 线段树优化dp 线段树上二分

    LINK:修改 题面就不放了 大致说一下做法.不愧是dls出的题 以前没见过这种类型的 不过还是自己dp的时候写丑了. 从这道题中得到一个结论 dp方程要写的优美一点 不过写的过丑 优化都优化不了. ...

  5. 4.9 省选模拟赛 圆圈游戏 树形dp set优化建图

    由于圆不存在相交的关系 所以包容关系形成了树的形态 其实是一个森林 不过加一个0点 就变成了树. 考虑对于每个圆都求出最近的包容它的点 即他的父亲.然后树形dp即可.暴力建图n^2. const in ...

  6. 【洛谷比赛】[LnOI2019]长脖子鹿省选模拟赛 T1 题解

    今天是[LnOI2019]长脖子鹿省选模拟赛的时间,小编表示考的不怎么样,改了半天也只会改第一题,那也先呈上题解吧. T1:P5248 [LnOI2019SP]快速多项式变换(FPT) 一看这题就很手 ...

  7. 【2019.7.26 NOIP模拟赛 T3】化学反应(reaction)(线段树优化建图+Tarjan缩点+拓扑排序)

    题意转化 考虑我们对于每一对激活关系建一条有向边,则对于每一个点,其答案就是其所能到达的点数. 于是,这个问题就被我们搬到了图上,成了一个图论题. 优化建图 考虑我们每次需要将一个区间向一个区间连边. ...

  8. 内存空间有限情况下的词频统计 Trie树 前缀树

    数据结构与算法专题--第十二题 Trie树 https://mp.weixin.qq.com/s/nndr2AcECuUatXrxd3MgCg

  9. 4.24 省选模拟赛 欧珀瑞特 主席树 可持久化trie树

    很容易的一道题目.大概.不过我空间计算失误MLE了 我草草的计算了一下没想到GG了. 关键的是 我学了一个dalao的空间回收的方法 但是弄巧成拙了. 题目没有明确指出 在任意时刻数组长度为有限制什么 ...

随机推荐

  1. HNOI 2014 米特运输(图论)

    HNOI 2014 米特运输 题目大意 给一棵树,每个点有自己的权值,要求更改一些点的权值,使得整棵树满足两个条件: 同一个父亲的所有子节点权值相同 父节点的取值为所有子节点的和 答案输出最少要更改的 ...

  2. Zip破解工具Fcrackzip使用简介

    0x00 fcrackzip简单介绍 fcrackzip是一款专门破解zip类型压缩文件密码的工具,工具小巧方便.破解速度快,能使用字典和指定字符集破解,适用于linux.mac osx 系统 0x0 ...

  3. Django---进阶8

    目录 前后端传输数据的编码格式(contentType) ajax发送json格式数据 ajax发送文件 django自带的序列化组件(drf做铺垫) ajax结合sweetalert 批量插入 分页 ...

  4. day27 面向对象

    day27 面向对象 目录 day27 面向对象 一.面相对象介绍 1 什么是对象 2 类于对象 二.实现面向对象编程 1 先定义类 2 属性访问 2.1 调用dict方法 2.2 类.属性 3 调用 ...

  5. 题解:2018级算法第六次上机 C6-危机合约

    题目描述 样例: 实现解释: 没想到你也是个刀客塔之二维DP 知识点: 动态规划,多条流水线调度?可以看做一种流水线调度 坑点: 输入内容的调整(*的特殊判定),开头结尾的调整策略 从题意可知,要做的 ...

  6. scrapy 基础组件专题(四):信号运用

    一.scrapy信号使用的简单实例 import scrapy from scrapy import signals from ccidcom.items import CcidcomItem cla ...

  7. AcWing 93. 递归实现组合型枚举

    AcWing 93. 递归实现组合型枚举 原题链接 从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案. 输入格式 两个整数 n,m ,在同一行用空格隔开. 输出格式 按照从小到大的 ...

  8. unity-TextAsset

    定义: 当把Text files导到unity,将会变成TextAsset. 支持的格式: .txt .html .htm .xml .bytes .json .csv .yaml .fnt 注意 不 ...

  9. Spring Boot 2.x基础教程:进程内缓存的使用与Cache注解详解

    随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一.Spring 3开始提供了强大的基于注解的缓 ...

  10. Go的100天之旅-04基础数据类型

    基础数据类型 在变量的定义中,我们讲了每个变量是有类型的,类型在计算机中是用来约束数据的解释.Go语言和其它计算机语言一样,提供丰富了丰富的数据类型,我们就来看看到底有哪些类型,同时也可以比较一下它和 ...