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\n");
    }else printf("YES\n");
    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分算法。
speech.gif posted on 2019-02-22 20:24 kgxpbqbyt 阅读( ...) 评论( ...) 编辑 收藏

猜你喜欢

转载自blog.csdn.net/qq_39759315/article/details/88403734