#数位dp
A
题意
- 两个字符串a,b,最终要将b添加到a上面
- 给定操作序列:如果是V就把b的当前位加入到a的开头,如果是D就加入到末尾
思路
- 模拟即可
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve(){
int n,m;
string a,b;
cin >> n;
cin >> a;
cin >> m;
cin >> b;
string op;
cin >> op;
for(int i=0;i<op.size();i++){
if(op[i]=='V'){
a=b[i]+a;
}else{
a=a+b[i];
}
}
cout << a << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
return 0;
}
B
题意
- 给定n,输出所有x使得n=x+y,y=x*(10^k)
思路
- 发现x+y其实就是11x,101x,1001x……,验证即可
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve(){
long long n;
cin >> n;
long long cnt=1;
vector<long long> ans;
for(int i=0;i<=18;i++){
cnt*=10;
if(n%(cnt+1)==0){
ans.push_back(n/(cnt+1));
}
}
cout << ans.size() << endl;
if(ans.size()){
sort(ans.begin(),ans.end());
for(auto i:ans){
cout << i <<' ';
}
cout << endl;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
return 0;
}
C1
题意
- 物品按
打包成堆,大小为
的物品代价为
- 刚好凑够n个物品,买最少堆,最少的代价是多少
思路
- 三进制拆分
- 表打大一点
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
long long qpow(long long a,long long b){
if(b<0) return 0;
long long ans=1;
while(b){
if(b&1){
ans*=a;
}
a*=a;
b>>=1;
}
return ans;
}
vector<long long> tb;
void solve(){
long long n;
cin >> n;
long long ans=0;
int cnt=0;
while(n>0){
ans+=(n%3)*tb[cnt];
n=n/3;
cnt++;
}
cout << ans << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
tb.push_back(3);
for(int i=1;qpow(3,i+1)+i*qpow(3,i-1)<2e18;i++){
tb.push_back(qpow(3,i+1)+i*qpow(3,i-1));
}
int T;
cin >> T;
while(T--){
solve();
}
return 0;
}
C2
题意
- 题意同上一题,改为凑够n个物品,最多k堆,最少代价
思路
- 先三进制拆分看要多少堆,如果现在已经超过k堆直接就寄了
- 余量的堆数可以给高位退位,如果手动一次次退位会T
- 需要一次性处理,记得开longlong,max_take不开会WA
代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
long long qpow(long long a,long long b){
if(b<0) return 0;
long long ans=1;
while(b){
if(b&1){
ans*=a;
}
a*=a;
b>>=1;
}
return ans;
}
vector<long long> tb;
void solve(){
long long n,k;
cin >> n >> k;
long long ans=0;
int cnt=0,sum=0;
vector<long long> tot(50);
while(n>0){
tot[cnt]+=n%3;
sum+=n%3;
n=n/3;
cnt++;
}
for(int i=31;i>=1;i--){
if(tot[i]==0) continue;
long long max_take=min(tot[i],(k-sum)/2);
if(max_take>0){
tot[i]-=max_take;
tot[i-1]+=3*max_take;
sum+=2*max_take;
}
// while(tot[i]>0 && sum+2 <= k){
// tot[i]--;
// tot[i-1]+=3;
// sum+=2;
// }
}
for(int i=0;i<tb.size();i++){
ans+=(tot[i]*tb[i]);
}
cout << (sum>k?-1:ans) << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
tb.push_back(3);
for(int i=1;qpow(3,i+1)+i*qpow(3,i-1)<2e18;i++){
tb.push_back(qpow(3,i+1)+i*qpow(3,i-1));
}
// for(int i=0;i<=10;i++){
// cout << tb[i] << endl;
// }
int T;
cin >> T;
while(T--){
solve();
}
return 0;
}
D
题意
- 无限长序列,从1开始不断向后延伸123456789101112
- 求到第k位的时候的和
思路
- 数位dp板子改一改就行
- 算一下最后到哪个数,残缺的部分暴力补全(常数时间)
- 前面完整的部分直接数位dp算
代码
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
/**
* f[i]表示i位数1~9出现的次数
* g[i]表示i位数占的位置
* f[i]=f[i-1]*10+10^(i-1)
* g[i]=i*9*10^(i-1)
*/
ull qpow(ull a,ull b){
if(b<0) return 0;
ull ans=1;
while(b){
if(b&1){
ans*=a;
}
a*=a;
b>>=1;
}
return ans;
}
ull ful[20],mi[20],g[20];
ull deal(ull n){
ull tmp=n;
int len=0;
ull a[20]={0};
ull ans[10]={0};
while(n) {
a[++len]=n%10;
n/=10;
}
for(int i=len;i>=1;i--) {
for (int j=1;j<10;j++) ans[j]+=ful[i-1]*a[i];
for (int j=1;j<a[i];j++) ans[j]+=mi[i-1];
tmp-=mi[i-1]*a[i];
ans[a[i]]+=tmp+1;
}
ull res=0;
for(int i=1;i<=9;i++) res+=i*ans[i];
return res;
}
void solve(){
ull k;
cin >> k;
int cnt;
for(cnt=1;k>=g[cnt];cnt++){
k-=g[cnt];
}
ull zheng=k/cnt;
ull can=k%cnt;
ull lst=qpow(10,cnt-1)+zheng-1;
ull ans=deal(lst);
string bas=to_string(qpow(10,cnt-1)+zheng);
for(int i=0;i<can;i++){
ans+=bas[i]-'0';
}
cout << ans << endl;
}
int main(){
mi[0]=1;
for(int i=1;i<=18;i++){
ful[i]=ful[i-1]*10+mi[i-1];
mi[i]=10*mi[i-1];
g[i]=9*i*mi[i-1];
// cout << i << ' ' << ful[i] << ' ' << g[i] << endl;
}
int T;
cin >> T;
while(T--){
solve();
}
return 0;
}
E
题意
- 两个序列,分别有n个元素和m个元素
- q次选择每次要选出最大的z个元素,且n中最多选x个,m中最多选y个,回答z个元素和
思路
- 朴素思考,要选出z个远足使得价值最大,肯定先考虑选最大的z个,但是最大的z个中可能某一个序列拿多了
- 如果某个序列拿多了,那最后的状态一定是这个序列拿上限k个,另一个序列拿z-k个
- 如果两个序列都没拿多,那直接输出最大的z个
- 对于获取若干个和,需要在
完成,所以两个序列分别排序维护前缀和,再额外维护一个所有元素的降序排序前缀和
代码
#include<bits/stdc++.h>
using namespace std;
using ll=unsigned long long;
#define pii pair<ll,ll>
bool cmp(pii a,pii b){
return a.first>b.first;
}
ll a[202020];
ll b[202020];
pii c[404040];
void solve(){
int cnt=0;
int n,m,q;
cin >> n >> m >> q ;
for(int i=1;i<=n;i++){
cin >> a[i];
c[++cnt]={a[i],1};
}
for(int i=1;i<=m;i++){
cin >> b[i] ;
c[++cnt]={b[i],0};
}
sort(a+1,a+n+1,greater());
sort(b+1,b+m+1,greater());
sort(c+1,c+cnt+1,cmp);
for(int i=1;i<=cnt;i++){
c[i].first+=c[i-1].first;
c[i].second+=c[i-1].second;
}
for(int i=1;i<=n;i++) a[i]+=a[i-1];
for(int i=1;i<=m;i++) b[i]+=b[i-1];
for(int i=0;i<q;i++){
int x,y,z;
cin >> x >> y >> z;
if(c[z].second>x){
cout << a[x]+b[z-x] << endl;
}else if(z-c[z].second>y){
cout << a[z-y]+b[y] << endl;
}else{
cout << c[z].first << endl;
}
}
}
int main(){
int t;
cin >> t;
while(t--){
solve();
}
return 0;
}