22th.Feb.2019
Posted kgxw0430
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了22th.Feb.2019相关的知识,希望对你有一定的参考价值。
T1
啊我死了
- 显然是一个2—sat模型,两个状态如果存在一个串是另一个的前缀,产生冲突所以对i与j+n建边(反向的也不要忘了),建完跑2-sat,如果有解就输出Yes,否则输出No。
- 直接暴力建边时间复杂度是n^2*siz的,有50分。所以需要优化建边的过程。
- 考虑把每一个状态串插入到trie树上,每一个状态串与它产生冲突的应该是它的所有祖先,这样复杂度优化到L*num,这个num大小取决于完全相同的串的个数,很抱歉,数据最后三组还是卡了。目前是88分。
- 100分算法目前不是太理解,先咕着。
Coding(88分)
#include<iostream>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
const int M=3e7+10;
int n,len,top,num,cnt,tot,tr[N][2],ver[M],Next[M],lin[N],dfn[N],low[N],ins[N],c[N],s[N],siz[N];
string ch[N];
vector<int>en[N];
void add(int x,int y){ver[++len]=y,Next[len]=lin[x],lin[x]=len;}
void insert(int id){
int ii;
int now=0,temp;if(id>n) ii=id-n;else ii=id;
for(int i=0;i<siz[ii];++i){
if(ch[ii][i]=='?'){
if(id>n) temp=1;
else temp=0;
}else temp=ch[ii][i]-'0';
if(!tr[now][temp]) tr[now][temp]=++tot;
now=tr[now][temp];
}en[now].push_back(id);
}
void find(int id){
int now=0,temp;int ii;
if(id>n) ii=id-n; else ii=id;
for(int i=0;i<siz[ii];++i){
for(int j=0;j<en[now].size();++j){
add(en[now][j],(id-n+2*n)%(2*n)),add(id,(en[now][j]-n+2*n)%(2*n));
}
if(ch[ii][i]=='?'){
if(id>n) temp=1;
else temp=0;
}else temp=ch[ii][i]-'0';
now=tr[now][temp];
//if(!now) break;
}
for(int j=0;j<en[now].size();++j){
if(en[now][j]!=id)
add(en[now][j],(id-n+2*n)%(2*n)),add(id,(en[now][j]-n+2*n)%(2*n));
}
}
void tarjan(int x){
s[++top]=x;ins[x]=1;
dfn[x]=low[x]=++num;
for(int i=lin[x];i;i=Next[i]){
int y=ver[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}else if(ins[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
cnt++;int y;
do{
y=s[top--];ins[y]=0;c[y]=cnt;
}while(x!=y);
}
}
bool two_SAT(){
for(int i=1;i<=n;++i)
if(c[i]==c[i+n])return false;
return true;
}
int main(){
freopen("1.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;++i){
cin>>ch[i];
siz[i]=ch[i].size();
insert(i);
insert(i+n);
}
for(int i=1;i<=2*n;++i){
find(i);
}
for(int i=1;i<=2*n;++i) if(!dfn[i]) tarjan(i);
if(!two_SAT()){
printf("NO
");
}else printf("YES
");
return 0;
}
T2
- 很显然,直接dfs复杂度为n!,有30分,不过我sb没开longlong,捆绑数据,所以原地去世了。
- n是14,其实并不大。想想搜索可不可以优化一下。
- n!其实也不小,所以想靠一个剪枝就A掉显然不太可能。
- 考虑折半枚举,你发现这道题是满足折半枚举的性质的。为什么呢?
- 我们现在先从1出发,枚举出任意走n/2个点的情况。然后把路径经过的点状压成一个数,和路径长度合到一起用map存起来。一个哈密顿回路其实与一个点到i+一个点到i是等价的(两条路径经过的点正好是1~n)。这样的话,复杂度就是n!/(n/2)!*log(n),显然可以过。
T3
- 暴力枚举出叶子节点的所有排列,然后每次暴力跑lca,记录边的使用次数,如果大于2就不合法。取所有合法解的最小值。复杂度n!*log(n)。记得从1~起点和终点到1这两条路径虽然求最大值的时候不需要管,但是考虑边的使用次数的时候是需要考虑的。(就是因为这个少了10分)。30分算法。
以上是关于22th.Feb.2019的主要内容,如果未能解决你的问题,请参考以下文章