传送门
思路:显然讨论一下 k k k的范围即可。
时间复杂度: O ( 1 ) O(1) O(1)
#include
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
int main(){
int n,k,x,y;
cin>>n>>k>>x>>y;
int ans=0;
if(n<=k) ans+=n*x;
else if(n>k) ans+=k*x+(n-k)*y;
cout<<ans;
return 0;
}
思路:按照题意模拟,特判 26 26 26个字母即可。
时间复杂度: O ( n + 26 ) O(n+26) O(n+26)
#include
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
int a[26];
int main(){
string s;
cin>>s;
for(int i=0;i<s.size();i++)
a[s[i]-'a']++;
int ok=1;
//for(int i=0;i<26;i++) printf("%d %d\n",i,a[i]);
for(int i=0;i<26;i++)
if(a[i]&1) {
ok=0;
break;
}
puts(ok?"Yes":"No");
return 0;
}
思路:背包问题。看着数据范围,妥妥的 d p dp dp。
我一开始设的是 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示前 i i i个数选 j j j个数和为 k k k的方案数。
显然有状态转移方程: d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j − 1 ] [ k − a [ i ] ] , ( k ≥ a [ i ] ) d p [ i ] [ j ] [ k ] + = d p [ i ] [ j − 1 ] [ k ] dp[i][j][k]=dp[i-1][j-1][k-a[i]],(k\geq a[i])\\dp[i][j][k]+=dp[i][j-1][k] dp[i][j][k]=dp[i−1][j−1][k−a[i]],(k≥a[i])dp[i][j][k]+=dp[i][j−1][k]
然后三重循环递推即可。
时间复杂度: O ( n 3 ) O(n^3) O(n3)
空间复杂度: O ( n 2 ) O(n^2) O(n2)
后来 A C AC AC了发现,这不就是背包的裸题吗。
显然可以进行优化第一维,只不过第二重和第三重循环改成倒序遍历以保证是 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1]的状态,不然顺序遍历会影响该状态。
空间复杂度: O ( n 2 ) O(n^2) O(n2)
p s : ps: ps:看来还是背包做少了。
#include
using namespace std;
typedef long long ll;
const int N=50+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
int a[N];
ll dp[N][N][3000],pre[1005];
int main(){
int n,k;
cin>>n>>k;
ll ans=0,sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
pre[i]=pre[i-1]+a[i];
}
for(int i=0;i<=n;i++) dp[i][0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++)
for(int k=1;k<=pre[i];k++){
if(k>=a[i])
dp[i][j][k]+=dp[i-1][j-1][k-a[i]];
dp[i][j][k]+=dp[i-1][j][k];
//printf("dp[%d][%d][%d]=%d\n",i,j,k,dp[i][j][k]);
}
}
for(int i=1;i<=n;i++)
ans+=dp[n][i][i*k];
printf("%lld\n",ans);
return 0;
}
空间优化后:
#include
using namespace std;
typedef long long ll;
const int N=50+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
int a[N],n,k;
ll dp[N][3000],ans;
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=i;j>=1;j--)
for(int k=2500;k>=a[i];k--) dp[j][k]+=dp[j-1][k-a[i]];
for(int i=1;i<=n;i++) ans+=dp[i][i*k];
printf("%lld\n",ans);
return 0;
}
思路:第一眼看数据范围 1 e 11 1e11 1e11,可以想到是 O ( n ) O(\sqrt{n}) O(n)左右的算法。
后来看题解是发现,利用数论知识分类讨论。
显然题意是要求我们将 n n n转化为 b b b进制然后对每位求和看是否等于 s s s,找到最小的 b b b。
即: a 0 × b 0 + a 1 × b 1 ⋯ + a k × b k = n a_0\times b^0+a_1\times b^1\dots+a_k\times b^k=n a0×b0+a1×b1⋯+ak×bk=n
∑ i = 0 k a i = s \sum\limits_{i=0}^ka_i=s i=0∑kai=s
显然当 k ≥ 2 k\geq 2 k≥2时,有 b 2 ≤ n → b ≤ n b^2\leq n\rightarrow b\leq\sqrt{n} b2≤n→b≤n
对于这部分直接暴力从小到大枚举即可。时间复杂度: O ( n l o g b n ) O(\sqrt{n}log_b\sqrt{n}) O(nlogbn)
对于 b > n b>\sqrt{n} b>n部分,显然 k k k最多是 1 1 1.
则有方程: a 0 + a 1 × b = n a 0 + a 1 = s → a 1 × ( b − 1 ) = n − s → b = n − s a 1 + 1 a_0+a_1\times b=n\\a_0+a_1=s\\ \rightarrow a_1\times(b-1)=n-s\\ \rightarrow b=\dfrac{n-s}{a_1}+1 a0+a1×b=na0+a1=s→a1×(b−1)=n−s→b=a1n−s+1
显然 a 1 < ( b − 1 ) , b > n , a 1 ≤ ( n − s ) a_1<(b-1),b>\sqrt{n},a_1\leq\sqrt{(n-s)} a1<(b−1),b>n,a1≤(n−s)
所以我们从大到小枚举 a 1 a_1 a1即可,等价于从小到大枚举 b b b。
貌似 a 1 a_1 a1的范围还可以再缩小,但是没必要了,够了。
总时间复杂度: O ( n l o g b n + n − s l o g b a 1 ) O(\sqrt{n}log_b\sqrt{n}+\sqrt{n-s}log_b a_1) O(nlogbn+n−slogba1)
#include
using namespace std;
typedef long long ll;
const int N=50+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
ll x;
ll fun(ll b){
ll sum=0;
while(x>=b){
sum+=x%b;
x/=b;
}
sum+=x;
return sum;
}
int main(){
ll n,s;
cin>>n>>s;
if(s==n) printf("%lld\n",n+1),exit(0);
if(s>n) return puts("-1"),0;
for(ll i=2;i*i<=n;i++){
x=n;
if(fun(i)==s){
printf("%lld\n",i);
return 0;
}
}
for(ll i=sqrt(n-s);i>=1;i--){
x=n;
if((n-s)%i==0){
ll b=(n-s)/i+1;
if(b>=2&&fun(b)==s) {
printf("%lld\n", b);
return 0;
}
}
}
puts("-1");
return 0;
}