231014校内赛
T1 进制转换
题解
经典题意越短越难系列
对于每一个询问的答案有个明显的限制,小于 \(\log_2n\)
所以在暴力计算答案时有一个小优化,大于了上界就直接退出循环
进一步思考如何优化暴力
对于 \(< \lceil \sqrt n \ \rceil\) 的数,可以直接暴力求解
对于 \(>\lceil \sqrt n \ \rceil\) 的数,因为转换进制后只有两位,所以可以枚举两位中的最高位,算出相应进制,如果进制大于了 \(k\) 那就当作 \(k\) 再处理
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
freopen("base.in","r",stdin);
freopen("base.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){
int n,k,ans = 33,lim;cin>>n>>k;
lim = ceil(sqrt(n));
for(int i = 2;i<=min(lim,k);i++){
int tmp = n,res = 0;
while(tmp){
res+=tmp%i;
tmp/=i;
if(res>=ans) break;
}
ans = min(ans,res);
}
int tmp = n/(lim+1);
for(int i = min(tmp,ans);i>=1;i--){
int base = n/i;
if(base>k){
int x = (n/(i+1))+1;
if(x>k) break;
base = k;
}
ans = min(ans,i+(n%base));
}
cout<<ans<<"\n";
}
return 0;
}
T2 序列计数
题解
对于这道题,很明显是可以暴力 \(dp\) 做 \(\mathcal O(n^3)\) 的部分分的题解写的,我不会这部分分
然后题解说的可以用前缀和优化掉一个 \(n\)
然后就是考虑容斥 \(dp\) ,有以下两种思路只会第一种:
- 对于一段 \(1\) 一段 \(0\) 的序列转为一段 \(1\) 加一个 \(0\) 的结构,可以看成序列 \(a_1,a_2,a_3,\ldots ,a_k\) ,其中 \(a_i\) 表示第 \(i\) 段 \(1\) 的长度
- 对于每一段 \(1\) 的段尾进行容斥
对于每一个 \(i\) 我们认为有 $0 \sim \frac n i $ 个不满足条件
对于每一个 \(f_i\) 我们表示连续段长度 \(\le i\) 的方案数,对于最后输出恰好为 \(i\) 也就是 \(f_i-f_{i-1}\)
对于所有情况来说,每一位都是随便选,所以就是 \(2^n\)
每一个串的长度为 \(i+1\) ,\(i\) 个 \(1\) 和一个 \(0\)
然后用容斥原理来出去我们算重复的和不满足限制条件的,算 \(f\) 两个循环都是在做这件事
对于式子我也不是很会推,以后再补
#include<bits/stdc++.h>
#define int long long
#define N 1000010
#define mod ((int)1e9+7)
using namespace std;
int n,fac[N],inv[N],f[N],pw[N];
int ksm(int x,int y){
int res = 1;
while(y){
if(y&1) res = res*x%mod;
x = x*x%mod;y>>=1;
}
return res;
}
int C(int x,int y){
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
fac[0] = pw[0] = 1;
for(int i = 1;i<=n;i++){
fac[i] = fac[i-1]*i%mod;
pw[i] = 2*pw[i-1]%mod;
}
inv[n] = ksm(fac[n],mod-2);
for(int i = n-1;i>=0;i--) inv[i] = inv[i+1]*(i+1)%mod;
for(int i = 1;i<=n+1;i++){
f[i] = pw[n];
for(int j = 1;j*(i+1)<=n;j++){
if(j&1) f[i] = (f[i]+mod-C(n-i*j,j)*pw[n-(i+1)*j]%mod)%mod;
else f[i] = (f[i]+C(n-i*j,j)*pw[n-(i+1)*j]%mod)%mod;
}
for(int j = 0;(i+1)*j+i<=n;j++){
if(j&1) f[i] = (f[i]+C(n-i*(j+1),j)*pw[n-(i+1)*j-i]%mod)%mod;
else f[i] = (f[i]+mod-C(n-i*(j+1),j)*pw[n-(i+1)*j-i]%mod)%mod;
}
}
for(int i = 1;i<=n+1;i++)
cout<<(f[i]-f[i-1]+mod)%mod<<" ";
return 0;
}
T3 01串
题解
不考虑动的位置,考虑不动的位置
相当于不动的位置顺序不变,动的位置按照任意顺序放在序列末尾
回文串和变化很大的顺序可以让人联想到区间 \(dp\)
我们只需要不动的那些位置不违背回文串的条件,并且剩下的位置可以满足可以凑成回文串即可
若 \(n\) 是偶数可以令 \(f_{l,r,k}\) 表示区间 \([l,r]\),选出了 \(k\) 对 \(1\),最多能选几对 \(0\) 使得选出的东西是回文串
转移和普通区间 \(dp\) 是类似的,需要注意奇数的情况类似,不过处理的细节不完全一样
时间复杂度 \(\mathcal O(n^3)\)。
#include<bits/stdc++.h>
#define N 1000010
#define mod 998244353
using namespace std;
int n,m,ans,a[N],f[310][310][310],num[2],sum[310][2],s[N];
char c[N];
int main(){
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>c+1;
for(int i = 1;i<=n;i++){
s[i] = c[i]%2;
num[s[i]]++;
}
if(n&1){
for(int i = 1;i<=n;i++)
if(num[s[i]]&1) f[i][i][0] = 1;
}else for(int i = 1;i<n;i++) f[i+1][i][0] = 0;
for(int i = 1;i<=n;i++){
sum[i][0] = sum[i-1][0];
sum[i][1] = sum[i-1][1];
sum[i][s[i]]++;
}
if(n%2==0&&num[0]%2==1){
cout<<"-1";
return 0;
}
if(num[0]&1) num[0]--;
if(num[1]&1) num[1]--;
for(int l = n;l;l--)
for(int r = l+1;r<=n;r++)
for(int k = 0;k<=r-l+1;k++){
f[l][r][k] = max(f[l][r-1][k],f[l+1][r][k]);
if(s[l]==s[r]&&k>=s[l]&&(n%2==0||f[l+1][r-1][k-s[l]]%2==1))
f[l][r][k] = max(f[l][r][k],f[l+1][r-1][k-s[l]]+2);
}
ans = n>>1;
for(int l = 1;l<=n;l++)
for(int r = l;r<=n;r++)
for(int k = 0;k<=n;k++)
if(f[l][r][k]){
int tmp1 = num[1]-k*2,tmp2 = num[0]-(f[l][r][k]-k*2-f[l][r][k]%2);
if(sum[l-1][1]>=(tmp1>>1)&&sum[l-1][0]>=(tmp2>>1))
ans = max(ans,f[l][r][k]+min(tmp1>>1,sum[l-1][1])+min(tmp2>>1,sum[l-1][0]));
}
cout<<n-ans;
return 0;
}
T4
照例,没碰
梦与现实间挣扎着,所求为何
你可以借走我的文章,但你借不走我的智慧 虽然我是傻逼本文来自博客园,作者:cztq,转载请注明原文链接:https://www.cnblogs.com/cztq/p/17773366.html