题解:P17094 [ICPC 2017 Qingdao R] Collecting Cents
lailai0916 · · 题解
题意简述
给定初始资金
解题思路
同一时刻、同一存期的两笔存款应当合并,因为:
还可以限制多年期存款的规模。若存期
因此,每种多年期存款的本金仅需枚举
状态记录当前现金
设当前还剩
随后将待到期序列左移,并加入本轮新存款。
直接保留全部动作仍然偏慢。写成
若动作
状态也可按支配关系删除。设状态
若差额始终非负,
每个待到期位置最多汇合
设每层保留 unsigned long long。
参考代码
#include <bits/stdc++.h>
using namespace std;
using ull=unsigned long long;
const int D=11;
const int B=6;
const ull base=1ULL<<B;
struct choice
{
ull key;
int sum,cst;
};
struct state
{
ull key,csh;
array<unsigned char,5> p;
};
struct full
{
ull key;
int sum,cst;
int pre[D];
};
int d,y,lim,rem;
int z[D];
bool vis[D];
vector<choice> ch[D][D][D];
vector<full> all;
vector<state> f,nf,st,buf;
vector<pair<ull,ull>> cv,tmp;
void dfs(int t,int sum)
{
if(t>lim)
{
int x=rem-sum,val=x>=0?x+x/d:x-(-x+d-1)/d;
full cur={0,sum,0,{}};
cur.pre[0]=val;
cur.cst=rem-val;
for(int i=2;i<=d;i++)
{
int q=i<=lim?z[i]+z[i]*i/d:0;
cur.key|=ull(q)<<(B*(i-2));
val+=q;
cur.pre[i-1]=val;
}
all.push_back(cur);
return;
}
for(int i=0;i<d;i++)
{
z[t]=i;
dfs(t+1,sum+i);
}
}
bool cover(const full &a,const full &b)
{
if(a.sum>b.sum)return 0;
for(int i=0;i<d;i++)if(a.pre[i]<b.pre[i])return 0;
return 1;
}
void init()
{
if(vis[d])return;
vis[d]=1;
for(lim=1;lim<=d;lim++)
{
for(rem=0;rem<d;rem++)
{
all.clear();
dfs(2,0);
vector<full> kp;
for(auto &cur:all)
{
bool bad=0;
for(auto &q:kp)
{
if(cover(q,cur))
{
bad=1;
break;
}
}
if(bad)continue;
kp.erase(remove_if(kp.begin(),kp.end(),[&](const full &q)
{
return cover(cur,q);
}),kp.end());
kp.push_back(cur);
}
for(auto &cur:kp)ch[d][lim][rem].push_back({cur.key,cur.sum,cur.cst});
}
}
}
void radix_sort()
{
int k=(B*(d-1)+9)/10,siz=cv.size();
tmp.resize(siz);
int cnt[1024];
for(int i=0;i<k;i++)
{
memset(cnt,0,sizeof cnt);
int sh=i*10;
for(auto &x:cv)cnt[(x.first>>sh)&1023]++;
for(int j=1;j<1024;j++)cnt[j]+=cnt[j-1];
for(int j=siz-1;j>=0;j--)tmp[--cnt[(cv[j].first>>sh)&1023]]=cv[j];
cv.swap(tmp);
}
}
void prune()
{
radix_sort();
st.clear();
int siz=cv.size();
for(int i=0;i<siz;)
{
int r=i+1;
ull key=cv[i].first,csh=cv[i].second;
while(r<siz&&cv[r].first==key)
{
csh=max(csh,cv[r].second);
r++;
}
state cur={key,csh,{}};
ull x=key;
for(int j=0;j<d-1;j++)
{
cur.p[j]=x%base;
x>>=B;
}
st.push_back(cur);
i=r;
}
ull mx=0,mn=ULLONG_MAX;
for(auto &cur:st)
{
mx=max(mx,cur.csh);
mn=min(mn,cur.csh);
}
vector<int> cnt(mx-mn+1);
for(auto &cur:st)cnt[mx-cur.csh]++;
int sum=0;
for(auto &x:cnt)
{
int cur=x;
x=sum;
sum+=cur;
}
buf.resize(st.size());
for(auto &cur:st)buf[cnt[mx-cur.csh]++]=cur;
st.swap(buf);
nf.clear();
for(auto &cur:st)
{
bool bad=0;
for(auto &q:nf)
{
ull dif=q.csh-cur.csh;
bool ok=1;
for(int i=0;i<d-1;i++)
{
dif+=dif/d;
if(q.p[i]>=cur.p[i])dif+=q.p[i]-cur.p[i];
else
{
ull val=ull(cur.p[i]-q.p[i]);
if(dif>=val)dif-=val;
else
{
ok=0;
break;
}
}
}
if(ok)
{
bad=1;
break;
}
}
if(!bad)nf.push_back(cur);
}
}
ull solve(ull n)
{
f.clear();
f.push_back({0,n,{}});
for(int i=0;i<y;i++)
{
cv.clear();
int m=min(d,y-i);
for(auto &cur:f)
{
ull q=cur.csh/d,bck=cur.key%base;
int r=int(cur.csh-q*d);
for(auto &x:ch[d][m][r])
{
if(cur.csh<ull(x.sum))continue;
ull csh=cur.csh+q-x.cst+bck,key=(cur.key>>B)+x.key;
cv.push_back({key,csh});
}
}
prune();
f.swap(nf);
}
return f[0].csh;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)
{
ull n;
cin>>n>>d>>y;
init();
cout<<solve(n)<<'\n';
}
return 0;
}