CF练习题18

发布时间 2023-11-03 11:51:50作者: LiQXing

这次的题都是什么怪物!!!

Short Colorful Strip

因为 \(n=m\),所以最终的形态一定是 \(n\) 的一个排列。

根据题意,发掘几个性质:

  1. 一个区间染色,一定最先对其中颜色最小的染色。
  2. 染色要求覆盖的点颜色完全相同。

对于第一次来说,先找到颜色为 \(1\) 的点,位置是 \(p\)

染色的区间是 \([l,r]\)\(l\le p\le r\)

所以我们可以把答案分成四个区间 \([1,l]\)\([l+1,p-1]\)\([p+1,r]\)\([r+1,n]\)

\(ans=\sum_{l=1}^{p-1}f_{1,l}\times f_{l+1,p-1}\times \sum_{r=p}^{n}f_{p+1,r}\times f_{r+1,n}\)

对于查询的每一个区间都可以用这种方法求得,所以做法就很显然了。

int n,m;
int a[N];
int f[505][505];
inline int dfs(int l,int r){
    if(~f[l][r])return f[l][r];
    if(l>=r)return f[l][r]=1;
    int p=l;
    up(i,l,r)if(a[i]<a[p])p=i;
    int ans1=0,ans2=0;
    up(i,l,p)ans1=(ans1+dfs(l,i-1)*dfs(i,p-1)%mod)%mod;
    up(i,p,r)ans2=(ans2+dfs(p+1,i)*dfs(i+1,r)%mod)%mod;
    return f[l][r]=ans1*ans2%mod;
}
signed main(){
    memset(f,-1,sizeof f);
    n=read();m=read();
    up(i,1,m)a[i]=read();
    cout<<dfs(1,m);
    return 0;
}

Long Colorful Strip

似乎可以用同样的做法,对于每一种颜色,找出它 最左端的位置和最右端的位置,枚举 \(l,r\) 然后把当前区间分成五段,进行区间 dp。

然后一看, \(m\le 10^6\) ,当场去世。

那么有没有什么优化呢,我们考虑一下对于一段颜色相同的区间,我们可以选择把其压缩成一个点,但是这样似乎优化的很玄学,想一想有没有什么性质可以利用。

考虑一下,我们每一次染色,似乎最多增加两个 \(c_i!=c_{i+1}\)

所以如果压缩后的 \(m\le 2n\),可以直接判断不合法。

复杂度 \(m^3\),但是能过。

int n,m;
int a[N],t;
int f[1505][1505];
int L[1540],R[1050];
inline int dfs(int l,int r){
    if(~f[l][r])return f[l][r];
    if(l>r)return 1;
    int &val=f[l][r];
	val=0;
	int minl=a[l];
	up(j,l,r)minl=min(minl,a[j]);
	int p=-1;
	int ans1=1;
	up(j,l,r){
		if(a[j]==minl) {
			if(p!=-1)ans1=ans1*dfs(p+1,j-1)%mod;
			p=j;
		}
	}
	int lans=0;
	up(j,l,L[minl]){
        lans=(lans+dfs(l,j-1)*dfs(j,L[minl]-1)%mod)%mod;
    }
	int rans=0;
	up(j,R[minl],r){
        rans=(rans+dfs(R[minl]+1,j)*dfs(j+1,r)%mod)%mod;
    }
	return val=ans1*lans%mod*rans%mod;
}
signed main(){
    memset(f,-1,sizeof f);
    n=read();m=read();
    int x,pre=0;
    up(i,1,m){
        x=read();
        if(x!=pre)a[++t]=x;
        pre=x;
    }
    if(t>2*n){
        puts("0");
        return 0;
    }
    up(i,1,n){
        int l=t+1,r=0;
        up(j,1,t){
            if(a[j]==i){
                l=min(l,j);
                r=max(r,j);
            }
        }
        up(j,l,r){
            if(a[j]<i){
                puts("0");
                return 0;
            }
        }
        L[i]=l;R[i]=r;
    }
    cout<<dfs(1,t);
    return 0;
}

Palindrome

很经典的一道题和性质。

最长回文子序列等于原串和反串的最长公共子序列。

但是求 LCS 是 \(n^2\) 的。

思考一下,因为全是小写字母,所以如果字符串的长度大于 \(2600\) 那么一定有一个所有字符都相同的符合条件的串。

不然的话就直接输出 LCS。

int n;
string s,t;
int f[2600][2600];
char ans[5050],ans2[5050];
signed main(){
    cin>>s;
    n=s.size();
    t=s;
    reverse(t.begin(),t.end());
    s=" "+s;t=" "+t;
    if(n<=2600){
        up(i,1,n){
            up(j,1,n){
                if(s[i]==t[j]){
                    f[i][j]=f[i-1][j-1]+1;
                }
                else{
                    f[i][j]=max(f[i-1][j],f[i][j-1]);
                }
            }
        }
        int i=n,j=n;
	    while(f[i][j]>0){
	    	if(s[i]==t[j]){
                ans[f[i][j]]=s[i];
                i--, j--;
            }
		    else{
			    if(f[i][j]==f[i-1][j]) i--;
			    else j--;
		    }
	    }
        if(f[n][n]<100){
            printf("%s",ans+1);
            return 0;
        }
        else{
            int t=0;
            up(i,1,50)ans2[++t]=ans[i];
            dn(i,49,0)ans2[++t]=ans[f[n][n]-i];
            printf("%s",ans2+1);
        }
    }
    else{
        up(j,'a','z'){
            int t=0;
            up(i,1,n){
                if(s[i]==j)t++;
            }
            if(t>=100){
                up(i,1,100)putchar(j);
                return 0;
            }
        }
    }
    return 0;
}

Constrained Tree

首先,我们已经知道了这个东西的先序遍历的顺序。

想一想如何判断一个点是在左子树还是右子树。

可以对每一个点求左子树的最小值,最大值,右子树的最小值,最大值。

然后递归求解。

int n,m;
int Lmax[N],Rmax[N],Lmin[N],Rmin[N];
int cnt=0;
bool flag=0;
vector<int>res;
inline void dfs(int l,int r){
    if(flag) return;
    ++cnt;
    if(Lmax[l]){
        if(Lmin[l]<=cnt)flag=1;
        dfs(cnt+1,Lmax[l]);
    }
    res.push_back(l);
    if(Rmax[l]){
        if(Rmin[l]<=cnt)flag=1;
        dfs(cnt+1,max(Rmax[l],r));
    } 
    else if(cnt<r)dfs(cnt+1,r);
}
signed main(){
    n=read();m=read();
    up(i,1,n){
        Lmin[i]=n+1;
        Rmin[i]=n+1;
    }
    int x,y;
    char s[10];
    up(i,1,m){
        x=read();y=read();
        if(x>=y){
            puts("IMPOSSIBLE");
            return 0;
        }
        scanf("%s",s+1);
        if(s[1]=='L'){
            Lmax[x]=max(Lmax[x],y);
            Lmin[x]=min(Lmin[x],y);
        }
        else{
            Rmax[x]=max(Rmax[x],y);
            Rmin[x]=min(Rmin[x],y);
        }
    }
    dfs(1,n);
    if(!flag){
        for(auto x:res){
            cout<<x<<" ";
        }
    }
    else puts("IMPOSSIBLE");
    return 0;
}

来一张阿卡多:

image

再来一张沃尔特:

image