2019.7.23模拟赛

T1 序列

写过的第一个迭代加深搜索,自然在考场上是想不出来的qwq

首先枚举答案,然后有一个核心函数:估价函数,用来估计离终点还有多远。

为了答案的准确性,估价函数\(g(x)\)和真正的距离函数\(f(x)\)需要满足\(g(x)\le f(x)\),同时为了速度,\(f(x)\)\(g(x)\)需要尽量接近。

在这题里面,我们注意到每换一次最多只会改变一对相邻数之差的绝对值,所以可以设\(g(x)\)表示相邻数之差不为1的个数。

然后,我们开始搜索。当发现\(dep+g(x)>mxdep\)的时候就直接返回,顺便再加个不能连续翻转同一块的剪枝,于是就跑得飞快了。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template<typename T>
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
temp void read(T &x)
{
    x=0;char ch=getchar(),t=0;
    while (ch>'9'||ch<'0') t|=(ch=='-'),ch=getchar();
    while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
    if (t) x=-x;
}
void file()
{
    #ifdef NTFAKIOI
    freopen("a.in","r",stdin);
    #endif
}
#define db double
#define ll long long
#define sz 25

int n;
vector<int>a,cur;
int mxdep;bool flg;
void dfs(int dep,int lst)
{
    if (dep>mxdep||flg) return;
    if (cur==a) return (void)(flg=1);
    int cc=0;
    rep(i,0,n-2) if (abs(cur[i+1]-cur[i])>1) ++cc;
    if (dep+cc>mxdep) return;
    rep(i,1,n-1) 
        if (i!=lst&&!flg) 
            reverse(cur.begin(),cur.begin()+i+1),dfs(dep+1,i),reverse(cur.begin(),cur.begin()+i+1);
}

void work()
{
    read(n);a.resize(n);cur.resize(n);
    rep(i,0,n-1) read(cur[i]),a[i]=i+1;
    flg=0;
    rep(i,0,1e9) 
    {
        mxdep=i;
        dfs(0,-1);
        if (flg) return (void)(cout<<i<<'\n');
    }
}

int main()
{
    file();
    int T;read(T);
    while (T--) work();
    return 0;
}

T2 轰炸

容易发现就是个最小反链覆盖,然而我把dilworth定理给忘了,而旁边的神仙个个都切了,于是就猜了个最长链的结论,顺便用数学归纳法伪证了一下,就过了。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template<typename T>
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
temp void read(T &x)
{
    x=0;char ch=getchar(),t=0;
    while (ch>'9'||ch<'0') t|=(ch=='-'),ch=getchar();
    while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
    if (t) x=-x;
}
void file()
{
    #ifdef NTFAKIOI
    freopen("a.in","r",stdin);
    #endif
}
#define db double
#define ll long long
#define sz 1010101
#define v edge[i].t
#define go(x) for (int i=head[x];i;i=edge[i].nxt)

int n,m;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
int deg[sz];
void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;++deg[t];}
int w[sz];
int dp[sz];

void solve()
{
    queue<int>q;
    rep(i,1,n) if (!deg[i]) q.push(i),dp[i]=w[i];
    while (!q.empty())
    {
        int x=q.front();q.pop();
        go(x)
        {
            chkmax(dp[v],dp[x]+w[v]);
            if (!--deg[v]) q.push(v);
        }
    }
    int ans=0;
    rep(i,1,n) chkmax(ans,dp[i]);
    cout<<ans;
}

namespace Build
{
    int n,m;
    struct hh{int t,nxt;}edge[sz<<1];
    int head[sz],ecnt;
    void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;}
    int dfn[sz],low[sz],cnt;
    stack<int>st;
    bool in[sz];
    int bel[sz],T;
    void tarjan(int x)
    {
        dfn[x]=low[x]=++cnt;in[x]=1;st.push(x);
        go(x) 
        {
            if (!dfn[v]) tarjan(v),chkmin(low[x],low[v]);
            else if (in[v]) chkmin(low[x],dfn[v]);
        }
        if (dfn[x]==low[x])
        {
            ++T;int y;
            do
            {
                y=st.top();st.pop();in[y]=0;
                ++w[T];bel[y]=T;
            }while (y!=x);
        }
    }
    void solve()
    {
        read(n),read(m);
        int x,y;
        rep(i,1,m) read(x),read(y),make_edge(x,y);
        rep(i,1,n) if (!dfn[i]) tarjan(i);
        rep(x,1,n) go(x) if (bel[x]!=bel[v]) ::make_edge(bel[x],bel[v]);
        ::n=T;
    }
}

int main()
{
    file();
    Build::solve();
    solve();
    return 0;
}

T3 字符串

前几天刚口胡过类似的题,所以基本上看完题就会了。

就是分类讨论,有三种可能:在左边,在右边,被切成两半。

所以我们建出原串和翻转+反转之后的串的AC自动机,就可以设\(dp_{i,S,x}\)表示前\(i\)位,搞定了\(S\)串,AC自动机上跑到了\(x\),的方案数,然后随便DP。

统计答案的时候判一下左右接在一起能不能搞出其他串,就可以了。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for (int i=(x);i<=(y);++i)
#define drep(i,y,x) for (int i=(y);i>=(x);--i)
#define temp template<typename T>
temp bool chkmax(T &x,T y){return x<y?x=y,1:0;}
temp bool chkmin(T &x,T y){return x>y?x=y,1:0;}
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
temp void read(T &x)
{
    x=0;char ch=getchar(),t=0;
    while (ch>'9'||ch<'0') t|=(ch=='-'),ch=getchar();
    while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
    if (t) x=-x;
}
void file()
{
    #ifdef NTFAKIOI
    freopen("a.in","r",stdin);
    #endif
}
#define db double
#define ll long long
#define sz 1266
#define mod 998244353ll

int n;
char s[8][sz];
int m;

ll dp[2][70][sz];

int root=0,cnt;
int ch[sz][2],fail[sz],msk[sz],msk2[sz];
void insert(int id,char *s)
{
    int n=strlen(s+1),x=root;
    rep(i,1,n)
    {
        int &v=ch[x][s[i]-'0'];
        if (!v) v=++cnt;
        x=v;
    }
    msk[x]|=(1<<(id-1));
}
void getfail()
{
    queue<int>q;
    rep(i,0,1) if (ch[0][i]) q.push(ch[0][i]);
    while (!q.empty())
    {
        int x=q.front();q.pop();
        msk[x]|=msk[fail[x]],msk2[x]|=msk2[fail[x]];
        rep(i,0,1)
        {
            int &v=ch[x][i];
            if (v) fail[v]=ch[fail[x]][i],q.push(v);
            else v=ch[fail[x]][i];
        }
    }
}

int main()
{
    file();
    read(n),read(m);
    rep(i,1,n)
    {
        cin>>(s[i]+1);
        insert(i,s[i]);
        int l=strlen(s[i]+1);
        rep(k,1,l) s[i][k]^=1;
        reverse(s[i]+1,s[i]+l+1);
        insert(i,s[i]);
        rep(k,1,l) s[i][k]^=1;
        reverse(s[i]+1,s[i]+l+1);
        rep(k,1,l-1)
        {
            bool flg=1;
            if (k<=l/2)
            {
                rep(t,1,k) flg&=(s[i][t]!=s[i][2*k-t+1]);
                if (!flg) continue;
                int x=root;
                drep(t,l,k+1) x=ch[x][(s[i][t]-'0')^1];
                msk2[x]|=(1<<(i-1));
            }
            else
            {
                rep(t,k+1,l) flg&=(s[i][t]!=s[i][2*k-t+1]);
                if (!flg) continue;
                int x=root;
                rep(t,1,k) x=ch[x][s[i][t]-'0'];
                msk2[x]|=(1<<(i-1));
            }
        }
    }
    getfail();
    int cur=0,lst=1;
    dp[0][0][0]=1;
    int S=(1<<n)-1;
    rep(i,0,m-1)
    {
        swap(cur,lst);
        rep(s,0,S) rep(x,0,cnt) dp[cur][s][x]=0;
        rep(s,0,S) rep(x,0,cnt) rep(t,0,1)
        {
            ll w=dp[lst][s][x];
            (dp[cur][s|msk[ch[x][t]]][ch[x][t]]+=w)%=mod;
        }
    }
    ll ans=0;
    rep(s,0,S) rep(x,0,cnt) if ((s|msk2[x])==S) (ans+=dp[cur][s][x])%=mod;
    cout<<ans;
    return 0;
}

转载于:https://www.cnblogs.com/p-b-p-b/p/11231999.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值