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\) 表示可以,转移方程:
(\(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;
}