问题 A: 招待
时间限制: 1 Sec 内存限制: 128 MB Special Judge
题目描述
请了两位奆老来为自己种树,小X也稍稍有些不好意思了,于是他准备了一些零食和饮料来招待奆老们。
然而,小X有强迫症,他希望自己和好基友们所有的零食和饮料的质量都要完全相同。
由于小X是一个奆老,所以他看不起普通商店里卖的电子秤,他决定自己做一个。
他的称重工具是一架由金子制成的天平,这架天平的精度非常高,可以达到纳克的标准,1g=109ng,小X会把物品放在天平的右侧,然后在天平的左侧和右侧都放上一些砝码,直至天平平衡。该天平的砝码是用钻石制成的,每个砝码的质量依次为1ng、3ng、9ng、27ng、81ng……,每个砝码的质量都是3的幂次(如3的6次幂表示为3^6=729),且各不相同。
由于小X是一个奆老,他有对各个物品未卜先知的能力,他会告诉你他的物品的质量,希望你给他一个方案,使得天平的两侧平衡。
输入
输入数据仅有一行包含一个正整数W,表示小X给出的物品的质量,重量单位是纳克(ng)
输出
输出数据共有两行,分别输出左右两端各个砝码及物体的质量,同一行砝码重量必须从小到大排序后按次序输出,第二行的第一个数必须先输出物体的质量,然后才是各个砝码的重量。相邻两个数之间必须严格用一个空格隔开。
注意:输入数据保证一定有解!如有多组解,输出任意一组即可!
样例输入
复制样例数据
67
样例输出
1 3 9 81 67 27
提示
小X给出的物品的质量为67pg,你可以在天平的左边放上4个砝码,重量依次为1,3,9,81总重量94ng,而右边放一个砝码质量为27ng,加上物体的重量67ng,恰好也是94ng,满足题目要求,此时天平的左右两端平衡。
对于20%的数据,W<=100
对于另外20%的数据,W<=10000,最多只用到5个砝码
对于另外20%的数据,W<=1000000,所有砝码都放在左边
对于另外20%的数据,W<=1000000
对于100%的数据,W<=1e15。
一开始想着dfs爆搜,然鹅想尽办法优化也只是能够处理1e13的数据、
知道是构造题后简单推一下就出来了,只要考虑n的三进制位置的数是怎么凑出来的就行了
/*author:revolIA*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+7;
int vis[50];
int digt[35];
int main(){
ll n;
scanf("%lld",&n);
vector<ll> L,R;
R.push_back(n);
int cnt = 0;
while(n){
digt[++cnt] = n%3;
n /= 3;
}
//for(int i=1;i<=cnt;i++)printf("%d",digt[i]);
ll tmp = 1;
for(int i=1;i<=cnt;i++,tmp *= 3)if(digt[i]){
if(digt[i] == 1){
if(!vis[i]){
L.push_back(tmp);
vis[i] = 1;
}else{
L.pop_back();
vis[i+1] = 1;
L.push_back(tmp*3);
R.push_back(tmp);
}
}else{
if(!vis[i]){
vis[i+1] = 1;
L.push_back(tmp*3);
R.push_back(tmp);
}else{
L.pop_back();
vis[i+1] = 1;
L.push_back(tmp*3);
}
}
}
for(int i=0;i<L.size();i++)printf("%lld%c",L[i],(i == L.size()-1)?'\n':' ');
for(int i=0;i<R.size();i++)printf("%lld%c",R[i],(i == R.size()-1)?'\n':' ');
return 0;
}
dfs双向爆搜、处理1e13可以在500ms左右,有个900ms左右的预处理
/*author:revolIA*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e7+4e6+17;
int head[maxn],Next[maxn],tot = 1;
pair<ll,ll> val[maxn];
ll Pow[35]={1LL};
ll out1[35],cnt1,ans[50];
ll out2[35],cnt2 = 1,mid = 13;
void Insert(ll x,ll y){//x ->> y
int u = y%maxn;
if(u<0)u += maxn;
val[tot] = {y,x};
Next[tot] = head[u];
head[u] = tot++;
}
ll Exist(ll y){
int u = y%maxn;
if(u<0)u += maxn;
for(int i=head[u];i;i=Next[i]){
if(val[i].first == y)
return val[i].second;
}
return -1LL;
}
int flag = 0,lmt = 1;
void dfs(int cur,ll x,ll y){
if(cur>mid){
if(Exist(y) == -1LL){
Insert(x,y);
}
return;
}
dfs(cur+1,x*3+0,y);//
dfs(cur+1,x*3+1,y+Pow[cur]);//+
dfs(cur+1,x*3+2,y-Pow[cur]);//-
}
void Find(int cur,ll y){
if(flag)return;
if(cur>=lmt){
if(Exist(y) != -1LL){
//printf(">> %lld -> %lld\n",y,Exist(y>=maxn?y%maxn:y));
flag = 1;
ll tmp = Exist(y);
for(int i=mid;i>=0;i--){
if(tmp%3 == 1){
out1[cnt1++] = Pow[i];
}else if(tmp%3 == 2){
out2[cnt2++] = Pow[i];
}
tmp /= 3;
}
for(int i=mid+1;i<cur;i++){
if(ans[i]<0){
out1[cnt1++] = -ans[i];
}else if(ans[i]>0){
out2[cnt2++] = ans[i];
}
}
sort(out1,out1+cnt1);
sort(out2+1,out2+cnt2);
ll sum1 = 0,sum2 = 0;
for(int i=0;i<cnt1;i++){
printf("%lld%c",out1[i],i==cnt1-1?'\n':' ');
sum1 += out1[i];
}
printf("%lld\n",sum1);
for(int i=0;i<cnt2;i++){
printf("%lld%c",out2[i],i==cnt2-1?'\n':' ');
sum2 += out2[i];
}
printf("%lld\n",sum2);
//printf("Yes\n");
}
return;
}
ans[cur] = -Pow[cur];
Find(cur+1,y-Pow[cur]);//-
if(flag)return;
ans[cur] = 0;
Find(cur+1,y);//
if(flag)return;
ans[cur] = Pow[cur];
Find(cur+1,y+Pow[cur]);//+
}
int main(){
for(int i=1;i<=33;i++)Pow[i] = Pow[i-1]*3;
//4782969
int x = clock();
dfs(0,0LL,0LL);
//printf("%d\n",clock()-x);
//printf("%d\n",tot);
printf("%dms\n",clock()-x);
/*int ans = 0;
for(int i=1;i<=4e7;i++){
int cnt = 0;
for(int j=head[i];j;j=Next[j])cnt++;
ans = max(ans,cnt);
}
printf("%d\n",ans);*/
x = clock();
ll n = 1e13;
//printf("%lld %lld\n",n,Exist(n));
//printf("%lld %lld\n",n,Exist(n));
//scanf("%lld",&n);
out2[0] = n;
lmt = mid+2;
while(!flag){
Find(mid+1,n);
++lmt;
}
printf("%dms\n",clock()-x);
return 0;
}