\(\cal T_1\) 我逝了

Description

\(\mathcal{P}\text{ortal.}\)

Solution

一些闲话:这题真的给我做泪目了,现在想起我只觉得自己是个 \(\rm nt\),建一个空间常数大的 \(\rm sam\),而且还只能 \(\rm dfs\) 搞出那张 \(\rm dag\),最后写出的代码像是在给人喂 💩,以 \(\mathtt{MLE}\)\(\mathtt{TLE}\) 零分的优势取得了辉煌的战绩 😢。

建出 \(\rm ac\) 自动机,利用路径压缩求出 \(n\) 个串的偏序集,然后就是 \(\text{[CTSC 2008] }\)祭祀 了,时间复杂度 \(\mathcal O(n^3)\).

Code

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
    T x=0; char s; bool f=0;
    while(!isdigit(s=getchar())) f|=(s=='-');
    for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    return f? -x: x;
}
template <class T>
inline void write(T x) {
    static int writ[50], w_tp=0;
    if(x<0) putchar('-'), x=-x;
    do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    while(putchar(writ[w_tp--]^48), w_tp);
}

# include <queue>
# include <vector>
# include <cstring>
# include <iostream>
using namespace std;

const int maxn = 2005;
const int maxl = 1e7+5;
const int infty = 0x3f3f3f3f;

bool vis[maxn]; int fa[maxl];
char s[maxl]; int fail[maxl];
queue <int> q; bool g[maxn][maxn];
int dep[maxn], Ref[maxl], ed[maxn];
int n, head[maxn], cnt=-1, S, T, arc[maxn];
struct edge { int nxt,to,w; } e[int(5e6)];

void addEdge(int u,int v,int w) {
    e[++cnt].to=v, e[cnt].nxt=head[u],
    e[cnt].w=w, head[u]=cnt;
    e[++cnt].to=u, e[cnt].nxt=head[v],
    e[cnt].w=0, head[v]=cnt;
}
bool bfs() {
    for(int i=1;i<=T;++i) 
        dep[i]=infty, arc[i]=-1;
    while(!q.empty()) q.pop(); int v;
    q.push(S), arc[S]=head[S], dep[S]=0;
    while(!q.empty()) {
        int u=q.front(); q.pop();
        for(int i=head[u]; ~i; i=e[i].nxt) 
            if(e[i].w && dep[v=e[i].to]==infty) {
                dep[v] = dep[u]+1,
                arc[v]=head[v], q.push(v);
                if(v==T) return true;
            }
    } return false;
}
int dfs(int u,int canFlow) {
    if(u==T) return canFlow;
    int sumFlow=0, d, v;
    for(int i=arc[u]; ~i; i=e[i].nxt) {
        arc[u] = i;
        if(e[i].w && dep[v=e[i].to]==dep[u]+1) {
            d = dfs(v, min(canFlow,e[i].w));
            if(!d) dep[v] = infty;
            e[i].w -= d, e[i^1].w += d,
            canFlow -= d, sumFlow += d;
            if(!canFlow) break;
        }
    } return sumFlow;
}
int dinic() {
    int ret=0; while(bfs()) 
    ret+=dfs(S,infty); return ret;
}

namespace mac {
    int t[maxl][2], idx;
    void ins(int now) {
        int p=0, len=strlen(s+1);
        for(int i=1;i<=len;++i) {
            bool d = (s[i]=='a'?0:1);
            if(!t[p][d]) t[p][d]=++idx, fa[idx]=p;
            p = t[p][d]; 
        } ed[now]=p, Ref[p]=now;
    }   
    void getfail() {
        while(!q.empty()) q.pop();
        for(int i=0;i<2;++i)
            if(t[0][i]) q.push(t[0][i]);
        while(!q.empty()) {
            int u=q.front(); q.pop();
            for(int i=0;i<2;++i)
                if(!t[u][i]) t[u][i]=t[fail[u]][i];
                else fail[t[u][i]]=t[fail[u]][i], q.push(t[u][i]);
        } Ref[0]=-1;
    }
}

void findS(int u) {
    if(vis[u]) return; vis[u]=1;
    for(int i=head[u]; ~i; i=e[i].nxt)
        if(!e[i].w && e[i].to!=T) findS(e[i].to);
}
bool inside(int u) {
    return !vis[u+n] && vis[u];
}

int main() {
    n=read(9); S=(n<<1)+1, T=S+1;
    memset(head,-1,sizeof(int)*(T+2)); 
    for(int i=1;i<=n;++i) 
        scanf("%s",s+1), mac::ins(i);
    mac::getfail(); vector <int> vec;
    for(int i=1;i<=n;++i)
        for(int j=ed[i]; j; j=fa[j]) {
            vec.clear(); int x=fail[j];
            for(; !Ref[x]; x=fail[x]) 
                vec.emplace_back(x);
            for(; !vec.empty(); vec.pop_back())
                fail[vec.back()] = x;
            fail[j] = x;
            if(j!=ed[i] && Ref[j]) x=j; 
            if(x) g[i][Ref[x]]=1;
        } 
    for(int k=1;k<=n;++k) for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j) g[i][j] |= (g[i][k]&g[k][j]);
    for(int i=1;i<=n;++i) 
        addEdge(S,i+n,1), addEdge(i,T,1);
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
        if(g[i][j]) addEdge(i+n,j,1);
    int M = dinic(); print(n-M,'\n'); 
    for(int i=1;i<=n;++i) if(!vis[i])
        if(e[(i<<2)-2].w) findS(i);
    for(int i=1;i<=n;++i)
        if(inside(i)) print(i,' '); puts("");
    return 0;
}

\(\cal T_2\) 排列数列

Description

\(\mathcal{P}\text{ortal.}\)

Solution

好神啊 qwq.

引入 \(\circ\) 符号,其中 \(a\circ b\) 表示排列 \(p_i=a_{b_i}\),接下来我们阐述 \(\circ\) 运算的一些性质:

  • \(\circ\) 运算满足结合律;
  • \(p^{-1}\)\(p\) 的逆,那么有 \(p\circ p^{-1}=p^{-1}\circ p=\epsilon\)
  • \((p\circ q)^{-1}=q^{-1}\circ p^{-1}\),这个可以推导一下 —— 记 \(f_i=p\circ q,g_i=q^{-1}\circ p^{-1}\),那么 \(f\circ g=p\circ q\circ q^{-1}\circ p^{-1}=\epsilon\),这利用了上文的结合律。

于是题目中的 \(f\) 函数可以被刻画成 \(f(p,q)_{p_i}=q_i\rightarrow f(p,q)\circ p=q\rightarrow f(p,q)=q\circ p^{-1}\)。由此可以进行递推:

\[a_1=p \]

\[a_2=q \]

\[a_3=q\circ p^{-1} \]

\[a_4=q\circ p^{-1}\circ q^{-1} \]

\[a_5=q\circ p^{-1}\circ q^{-1}\circ p\circ q^{-1} \]

\[\begin{align} a_6&=q\circ p^{-1}\circ q^{-1}\circ p\circ q^{-1}\circ (q\circ p\circ q^{-1})\\ &=q\circ p^{-1}\circ q^{-1}\circ p\circ p\circ q^{-1} \end{align} \]

由于 \(k\) 很大,所以尝试找一下规律 —— 观察到 \(a_5,a_6\) 都有一个共同的较大的一坨,不妨令其为 \(A=q\circ p^{-1}\circ q^{-1}\circ p\),那么

\[a_7=A\circ p\circ q^{-1}\circ q\circ A^{-1}=A\circ p\circ A^{-1} \]

\[a_8=A\circ p\circ A^{-1}\circ q\circ p^{-1}\circ A^{-1} \]

由于 \(A^{-1}=p^{-1}\circ q\circ p\circ q^{-1}\),所以 \(a_8=A\circ q\circ A^{-1}\).

事实上,\(a_7,a_8\) 开始与末尾的 \(A,A^{-1}\) 结构可以进行抵消

\[a_9=A\circ q\circ A^{-1}\circ A\circ p^{-1}\circ A^{-1}=A\circ q\circ p^{-1}\circ A^{-1} \]

我们惊奇地发现,中间的 \(q\circ p^{-1}\) 就是 \(a_3\)!而 \(a_7,a_8\) 中间的部分就是 \(a_1,a_2\)。所以,我们可以利用此递推再加上快速幂解决此题,复杂度 \(\mathcal O(n\log k)\).

Code

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while(!isdigit(s=getchar())) f|=(s=='-');
	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
	return f? -x: x;
}
template <class T>
inline void write(T x) {
	static int writ[50], w_tp=0;
	if(x<0) putchar('-'), x=-x;
	do writ[++w_tp] = x-x/10*10, x/=10; while(x);
	while(putchar(writ[w_tp--]^48), w_tp);
}

const int maxn = 1e5+5;

int n, k;
struct perm {
    int a[maxn];
    perm operator * (const perm& t) const {
        perm r; for(int i=1;i<=n;++i) 
        r.a[i]=a[t.a[i]]; return r;
    }
    void output() { for(int i=1;i<=n;++i) print(a[i],' '); }
    void readPerm() { for(int i=1;i<=n;++i) a[i]=read(9); }
} p, q, ans, A;

perm inv(const perm& t) {
    perm r; for(int i=1;i<=n;++i)
    r.a[t.a[i]]=i; return r;
}
perm qkpow(perm t,int y) {
    perm r; for(int i=1;i<=n;++i) r.a[i]=i;
    for(; y; y>>=1, t=t*t)
        if(y&1) r=r*t; return r;
}

int main() {
    n=read(9), k=read(9)-1;
    p.readPerm(), q.readPerm();
    A = q*inv(p)*inv(q)*p;
    switch(k%6) {
        case 0: ans=p; break;
        case 1: ans=q; break;
        case 2: ans=q*inv(p); break;
        case 3: ans=q*inv(p)*inv(q); break;
        case 4: ans=A*inv(q); break;
        case 5: ans=A*p*inv(q); break;
    }
    ans = qkpow(A,k/6)*ans*qkpow(inv(A),k/6);
    ans.output();
    return 0;
}

\(\cal T_3\) 同步子序列

Description

\(\mathcal{P}\text{ortal.}\)

Solution

这次真的是复读题解了

\(b\)\(+1\)\(a\)\(-1\),那么在序列中找出若干段前缀和为零的前缀,可以将序列划分成若干段互不影响的区间。我们分开考虑每一段,这有什么好处呢?由于一段的每个前缀和都不为零,所以一定有所有前缀和均大于零或小于零,也就是说对于一对 \(a,b\),在同一段内一定满足 \(a\) 出现在 \(b\) 之前或 \(b\) 出现在 \(a\) 之前。对这两种情况分类讨论:

  • \(a\) 出现在 \(b\) 之前。容易得知第一个字符一定为 \(a\),考虑这个 \(a\) 匹配的 \(b\),显然会将它们之间的所有 \(a\) 都删掉以保证字典序最大。递归进行此操作即可;

  • \(b\) 出现在 \(a\) 之前。最终序列一定是一段 \(b\),考虑最大化其长度。可以求出最大前缀和,记其为 \(\rm val\),且匹配到的 \(b\) 的位置为 \(p\)。这里有个结论:

    最优解一定是只删除 \(p\) 之前的 \(a\),保留后面的所有字符。

    首先 \(p\) 后面紧挨的一段 \(a\) 肯定不能被删除,否则不满足最大前缀和的约束。紧接着后面就是 \(b\),而删除这个 \(b\) 是不优的。结论得证。

    由于可能有多个位置 \(p_1,p_2\) 都满足前缀和为 \(\rm val\) 的条件,且后面部分不会被删掉了,所以可以直接比较 \((p_1,n],(p_2,n]\) 来得知选哪个字典序更大。

最后考虑将区间合起来,不过选取所有区间并不见得是最优解。所以还要维护一个类似单调栈的东西,每次判断一下新加入字符串 \(s\) 和栈顶接上 \(s\) 的字典序大小关系。时间复杂度 \(\mathcal O(n^2)\).

Code

# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while(!isdigit(s=getchar())) f|=(s=='-');
	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
	return f? -x: x;
}
template <class T>
inline void write(T x) {
	static int writ[50], w_tp=0;
	if(x<0) putchar('-'), x=-x;
	do writ[++w_tp] = x-x/10*10, x/=10; while(x);
	while(putchar(writ[w_tp--]^48), w_tp);
}

# include <iostream>
using namespace std;

const int maxn = 6005;

char s[maxn];
string S[maxn], t;
int n, presum, pos, tp;

void update(int st,int r) {
    if(pos==-1) return pos=st, void();
    for(int i=1;i<=r-st+1;++i)
        if(s[i+pos-1]>s[i+st-1]) return;
        else if(s[i+pos-1]<s[i+st-1]) return pos=st, void();
}
void makeString(int l,int r) {
    t="", pos=-1; int j, val=0, cur=0, tot=0;
    if(s[l]=='a') { 
        for(int i=l;i<=r;++i) if(s[i]^'b') {
            j=i; int tmp=tot;
            while(233) { ++ j;
                if(s[j]=='a') ++tot;
                else if(!tmp) break;
                else --tmp, --tot;
            } t+='a', t+='b', i=j;
        } else --tot;
    } else {
        for(int i=l;i<=r;++i) 
            cur += (s[i]=='a'? -1: 1),
            val = max(val, cur); cur=0;
        for(int i=l;i<=r;++i) {
            cur += (s[i]=='a'? -1: 1);
            if(cur==val) update(i+1,r);
        }
        for(int i=1;i<=val;++i) t+='b';
        for(int i=pos;i<=r;++i) t+=s[i];
    }
}

bool cmp(const string& s,const string& t) {
    int len = t.length();
    for(int i=0;i<len;++i)
        if(s[i]<t[i]) return true;
        else if(s[i]>t[i]) return false;
    return false;
}

int main() {
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    n=(read(9)<<1), scanf("%s",s+1);
    for(int i=1, j=1; i<=n; ++i) {
        presum += (s[i]=='b'? 1: -1);
        if(presum==0) makeString(j,i);
        else continue; j = i+1;
        while(tp && cmp(S[tp]+t,t)) --tp;
        S[++tp] = t;
    }
    for(int i=1;i<=tp;++i) cout << S[i]; puts("");
    return 0;
}
posted on 2022-07-12 11:28  Oxide  阅读(108)  评论(0编辑  收藏  举报