好题 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. css常用的简写技巧_css background简写、css border 简写、css font属性简写等

    css样式中有很多简写方式,比如:设置背景,字体,边框,盒子等.我们都可以把css代码合并为一行,这篇文章将总结有哪些属性支持css简写. 1.背景background属性 background-co ...

  2. When Lambo with Howdoo

    原文链接:https://howdoo.io/when-lambo/ 为了庆祝即将推出的革命性新社交媒体平台Howdoo以及我们令人惊喜的合作伙伴关系和社区,我们正在发起一项竞赛,以最终回答“When ...

  3. Mysql 常用语句实战(1)

    前置 sql 语句 用来创建表.插入数据 DROP TABLE IF EXISTS `emp`; CREATE TABLE `emp` ( `id` int(11) NOT NULL COMMENT ...

  4. nuxt的使用中碰到的问题

    使用npm run generate生成静态页面部署 如果不是部署在域名的根目录下,则需要在nuxt.config.js中添加 // nuxt.config.js export default { r ...

  5. classpath路径(转)

    src不是classpath, WEB-INF/classes,lib才是classpathWEB-INF/ 是资源目录, 客户端不能直接访问, 这话是没错,不过现在的IDE编译器在编译时会把src下 ...

  6. 数据可视化之powerBI基础(八)PowerBI的表格,你真的会用吗

    https://zhuanlan.zhihu.com/p/64413000 在PowerBI的可视化对象中,还有两个「表格」对象,表格的作用不仅可以在报表提供明细数据,还经常用来测试度量值的返回结果, ...

  7. Flask 基础组件(四):模板

    1.模板的使用 1.1  语法 1.1.1 流程控制 逻辑语法 Jinja2模板语言中的 for {% for foo in g %} {% endfor %} Jinja2模板语言中的 if {% ...

  8. 【五学x红小豆xRS】两边三地大联动-句型

    <第五共和国> Tohara LY Sara'm, Sabang Chua Setuk KS Kareh Moh Induree Junchi Chueh? 阁下!和这样的虫豸在一起,怎么 ...

  9. 使用 JS 开发 Github Actions 实现自动部署前后台项目到自己服务器

    不想看前面这么多废话的可以直接跳到具体实现 Github Actions 是什么? 说到 Github Actions 不得不提一下. 持续集成(continuous integration):高质量 ...

  10. Ethical Hacking - GAINING ACCESS(22)

    CLIENT SIDE ATTACKS - BeEf Framework Browser Exploitation Framework allowing us to launch a number o ...