YL 模拟赛总结 5

Posted on 2024-03-02 21:30  _XOFqwq  阅读(6)  评论(0编辑  收藏  举报

Problem


T1

\(m\) 个人中间必定有 \(m-1\) 个空位,剩下 \(n-m+1\) 个位置可以随意放人,则方案数为 \(A^{m}_{n-m+1}\)

T2

考虑进行 \(dp\)

状态:令 \(dp_{i,j}\) 表示字符串 \(S_{i \sim j}\) 要变成回文串需要添加的最少字符数。

转移:

枚举区间左端点 \(l\) 和长度 \(k\),右端点为 \(r\)。显然需要进行分类讨论:

\(S_l=S_r\),则不需要添加字符,于是 \(dp_{l,r}=dp_{l+1,r-1}\)

否则,可以在 \(l\) 前或 \(r\) 之后添加字符,于是 \(dp_{l,r}=\min(dp_{l+1,r},dp_{l,r-1})+1\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

string s;
int dp[1031][1031];

signed main(){
    cin>>s;
    for(int k=2;k<=s.size();k++){
        for(int l=0;l+k-1<s.size();l++){
            int r=l+k-1;
            if(s[l]==s[r]) dp[l][r]=dp[l+1][r-1];
            else dp[l][r]=min(dp[l+1][r],dp[l][r-1])+1;
        }
    }
    cout<<dp[0][s.size()-1];
    return 0;
}

T3

我们可以先写一个 \(\text{brute-force}\),预处理出 \(1 \sim 9\) 位数的各个数位上的数之和的前缀和数组。

然后对于输入的 \(n\),若其数位为 \(s\),则答案为 \(s\) 位数的最小数到 \(n\) 的各个数位上的数之和 \(+ \ sum_{s-1}\),其中 \(sum\) 是我们刚刚求出来的前缀和数组。

但是这样还是会超时。进一步的,若 \(n\) 更靠近 \(s\) 位数的最小数,则使用上述算法,否则答案为 \(sum_s - s\) 位数的最小数到 \(n\) 的各个数位上的数之和 \(+ \ sum_{s-1}\)

#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;

int n;
int t[31]={0,45,900,13500,180000,2250000,27000000,315000000,3600000000,40500000000,1};

//省略快读

int ws(int x){
	int res=0;
	while(x) res++,x/=10;
	return res;
}
int f(int x){
    int res=0;
    while(x) res+=x%10,x/=10;
    return res;
}
int gz(int x){
	int res=0;
	while(x) res+=pow(10,x-1)*9,x--;
	return res;
}

signed main(){
	//freopen("count.in","r",stdin);
	//freopen("count.out","w",stdout);
	n=read();
	int res=0,s=ws(n),w=gz(s); //cout<<gz(s)<<'\n';
	if(w-n>n-pow(10,s-1)){
		for(int i=pow(10,s-1);i<=n;i++) res+=f(i);
		write(res+t[s-1]);
	}
	else{
		for(int i=w;i>n;i--) res+=f(i);
		write(t[s]-res);
	}
	return 0;
}

T4

还是考虑 \(dp\)

状态:令 \(dp_{i,j}\) 表示大小为 \(i\)\(j\) 列的矩阵是否能必胜。

转移:

我们首先需要预处理出 \(pre\) 数组,其中 \(pre_{0,i,j}\) 表示大小为 \(i\)\(j\) 列的矩阵是否可以删除最后一列,\(0\) 表示可以,转移方程:

\[pre_{0,i,j}=(pre_{0,i-1,j}+mp_{i,j}) \bmod 2 \]

\(mp_{i,j}\) 是输入的矩阵)

\(pre_{1,i,j}\) 同理可得。

然后进行 \(dfs\)

若当前矩阵的最后一列能被删除且删除之后的矩阵未被计算过,则对删去最后一行继续进行 \(dfs\),最后一行同理。

递归返回的时候,若当前矩阵最后一列能被删除,则令 \(a=!dp_{x,y}\)\(x,y\) 为当前矩阵的规模),最后一行同理,用 \(b\) 记录。

为什么要取反呢?因为递归返回时,若当前矩阵能必胜,则上一层的矩阵是另一个人进行操作,所以不能必胜。必败的情况同理。

最后,将 \(dp_{x,y}\) 设为 \(a \operatorname{or} b\) 即可。

#include<bits/stdc++.h>
using namespace std;

int t,n,mark;
int mp[1031][1031];
int pre[2][1031][1031];
int dp[1031][1031];
bool vis[1031][1031];

void dfs(int x,int y){
    vis[x][y]=mark;
    bool a=0,b=0;
    if(!x||!y){ dp[x][y]=0; return; }
    if(!pre[0][x][y]&&vis[x][y-1]!=mark) dfs(x,y-1);
    if(!pre[1][x][y]&&vis[x-1][y]!=mark) dfs(x-1,y);
    if(!pre[0][x][y]) a=!dp[x][y-1];
    if(!pre[1][x][y]) b=!dp[x-1][y];
    dp[x][y]=(a||b);
}

void solve(){
    cin>>n,mark++;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            cin>>mp[i][j];
            pre[0][i][j]=(pre[0][i-1][j]+mp[i][j])%2;
            pre[1][i][j]=(pre[1][i][j-1]+mp[i][j])%2;
        }
    dfs(n,n);
    cout<<(dp[n][n]?"W\n":"L\n");
}

int main(){
    ios::sync_with_stdio(0);
    cin>>t;
    while(t--) solve();
    return 0;
}