题目大意:

  给 n ( n<=1e5 ) 个数 \( a_i \) (\( a_i \) <=1e5),需要构造 n 个实数使得它们的和是 1 ,并且第 i 个实数必须小数点后恰好有 \( a_i \) 个有意义的数位。有意义的数位指的是到最后一个非0位为止的数位。

Subtask 1 (17 pts) : n<=100 , \( a_i \) <=10

Subtask 2 (21 pts) : n<=1e5 , all \( a_i \) are equal

Subtask 3 (25 pts) : n<=1000 , \( \sum a_i \) <=1000

Subtask 4 (37 pts) : 没有特殊性质

  想到一个构造方法。就是数位限制按从多到少排序, \( a_i \) 相同的一些实数,每个都是 0.000..001 ,只有最后一个用来补齐,使得至今为止的和是满足下一个较小的 \( a_i \) 的限制的。

  如果补齐恰好使得该实数的数位少于 \( a_i \) ,那么只需要让 \( a_i \) 相同的某个实数从 0.000..001 变成 0.000..002 ,该实数就能减少 0.000..001 ,从而数位符合要求。

  如果需要做上面那个操作,但 \( a_i \) 是该值的只有该实数一个,那么就无解。其实这里感觉有些不对,因为可以调更之前的一些数,但就这样写也能过。

  可惜 double 无法做到 1e5 的精度。所以用 long long ,只有 17 分。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
const int N=1e5+;
int n,a[N],mx,g[]; ll ans[N],bin[];
struct Node{
int a,id;
bool operator< (const Node &b)const
{return a>b.a;}
}c[N];
bool chk(int x)
{
ll tp=ans[c[x].id]; int lm=c[x].a,cnt=;
while(tp&&tp%==)tp/=,cnt++;
return mx-cnt!=lm;
}
int main()
{
n=rdn();
for(int i=;i<=n;i++)
{
a[i]=c[i].a=rdn(); c[i].id=i;
mx=Mx(mx,a[i]);
}
bin[]=;
for(int i=;i<=mx;i++)bin[i]=bin[i-]*;
sort(c+,c+n+);
ll bs,bs2=bin[mx-c[].a],lj=;
for(int i=;i<=n;i++)
{
bs=bs2;
int j=i; ans[c[i].id]=bs; lj+=bs;
while(j<n&&c[j+].a==c[j].a)
j++, ans[c[j].id]=bs, lj+=bs;
bs2=bin[mx-c[j+].a];
ll tp=bs2;
while(tp<lj)tp+=bs2;
swap(tp,lj); tp=lj-tp;
ans[c[i].id]+=tp;
if(chk(i))
{
if(j==i){puts("NO");return ;}
ans[c[i].id]-=bs; ans[c[i+].id]+=bs;
}
i=j;//
}
if(lj>bin[mx]){puts("NO");return ;}
puts("YES");
for(int i=;i<=n;i++)
{
printf("0.");
while(ans[i]%==)ans[i]/=;
int t=;
while(ans[i])g[++t]=ans[i]%,ans[i]/=;
for(int j=t+;j<=a[i];j++)g[j]=;
for(int j=a[i];j;j--)putchar(g[j]+'');
puts("");
}
return ;
}

  然后看了这个题解:https://blog.csdn.net/lycheng1215/article/details/80246134

  用高精度实现就可以了。实现方法也是借鉴那个题解的……

  大概就是用 vector 的 rem[ i ] 表示第 i 种 a 的用来补齐的那个实数的值。记录成一个大数,末位就是小数点后第 \( a_i \) 位。

  先算出每个 a 对应了多少个实数,然后从大到小遍历 a ,记录一个 lj 表示之前的数已经带来的贡献。lj 是一个 int 类型的,个位表示小数点后第 \( a_i \) 位。

  设 ct 表示当前的 a 对应了 ct 个实数,那么它们都填 1 ,再算上之前补上来的,和就是 tmp = lj+ct ;

  枚举数位从 \( a_i \) 到 \( a_{i+1} \) (\( a_{i+1} < a_i \) ),tmp 一直 /10 ,做完之后的 tmp 就是当前的和进到下一个 a 是多少了;

  同时记录一个 hs 表示 tmp /10 的过程中,有没有非0位;如果有的话,做完 /10 操作之后的 tmp 就需要再 +1 ,因为补足是要上取整的。

  然后 rem[ i ] 的初值就是:先有 \( a_i - a_{i+1} \) 个 0 ,然后是一个数 tmp+hs 。

  然后用 rem[ i ] 减去刚才的 lj ,再减去 ct-1 ,如果 (ct-1)%10 == rem[ 0 ] (这里的 rem[ 0 ] 是减去 lj 之后的,相等表示 ct-1 个实数都填 1 之后,补齐的那个实数会缺少一些数位),rem[ i ] 再减去 1 即可,同时给 i 打一个标记表示它有一个填 2 的实数。

  然后 lj 就变成刚才的那个 tmp+hs 即可。

  lj 是可以用 int 类型的,因为每次加 ct ,最多在加一个 hs ,还会不断 /10 ,不会超过 2e5 ; 之所以 rem[ ] 需要用高精度,是因为 \( a_i \) 到 \( a_{i+1} \) 可能有 1e5 个位置,用来补齐的那个实数就可能有这么多不平凡的数位。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define pb push_back
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=1e5+;
int n,mx,a[N],ct[N],pr[N]; bool vis[N];
vector<int> rem[N];
void print(int cr,int fx)
{
printf("0.");
for(int i=;i<a[cr];i++)
putchar('');
printf("%d\n",fx);
}
void printx(int cr)
{
int bh=a[cr],siz=rem[bh].size();
printf("0.");
for(int i=a[cr]-siz;i;i--)
putchar('');
for(int i=siz-;i>=;i--)//-- not ++!!!
printf("%d",rem[bh][i]);
puts("");
}
void add_frs(int cr)
{
int siz=rem[cr].size()-;
for(int i=;i<siz;i++)
if(rem[cr][i]>)
{
rem[cr][i+]+=rem[cr][i]/;
rem[cr][i]%=;
}
while(rem[cr][siz]>)
{
rem[cr].pb(rem[cr][siz]/);
rem[cr][siz]%=; siz++;
}
}
void dec_frs(int cr)
{
int siz=rem[cr].size()-;
for(int i=;i<siz;i++)
if(rem[cr][i]<)
{
int tp=-rem[cr][i];
tp=tp/+(tp%?:);
rem[cr][i]+=*tp;
rem[cr][i+]-=tp;
}
while(!rem[cr][siz])
rem[cr].pop_back(), siz--;
}
void solve()
{
int lj=;
for(int i=mx;i;i=pr[i])
{
int tmp=lj+ct[i]; bool hs=;
for(int j=i;j>pr[i]&&tmp;j--)//go to lst pos
{ hs|=tmp%; tmp/=;}
rem[i].resize(i-pr[i]);//some 0 based a[i]
rem[i].pb(tmp+hs); add_frs(i);
int tp=lj; lj=tmp+hs;
for(int j=;tp;j++)
{ rem[i][j]-=tp%; tp/=;}
dec_frs(i);
tp=ct[i]-;
if(tp%==rem[i][])
{
if(ct[i]==){puts("NO");return;}
tp++;vis[i]=;//some is 2
}
for(int j=;tp;j++)
{ rem[i][j]-=tp%; tp/=;}
dec_frs(i);
}
if(lj>){puts("NO");return;}
puts("YES");
for(int i=;i<=n;i++)
{
ct[a[i]]--;
if(vis[a[i]])
{ vis[a[i]]=; print(i,);}
else if(ct[a[i]])
{ print(i,);}
else printx(i);
}
}
int main()
{
n=rdn();
for(int i=;i<=n;i++)
{ a[i]=rdn(); ct[a[i]]++;}
for(int i=1e5;i;i--)if(ct[i]){mx=i;break;}
for(int i=,lst=;i<=mx;i++)
{
pr[i]=lst; if(ct[i])lst=i;//if()!!
}
solve();
return ;
}

APIO2019 练习赛 Wedding cake——思路+高精度的更多相关文章

  1. HDU 4762 Cut the Cake(高精度)

    Cut the Cake Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tota ...

  2. HDU4762 Cut the Cake

    HDU4762 Cut the Cake 思路:公式:n/m(n-1) //package acm; import java.awt.Container; import java.awt.geom.A ...

  3. Hdoj 2289.Cup 题解

    Problem Description The WHU ACM Team has a big cup, with which every member drinks water. Now, we kn ...

  4. 3D Food Printing【3D食物打印】

    3D Food Printing There's new frontier in 3D printing that's begining to come into focus: food. 3D打印的 ...

  5. 洛谷 P1037 产生数

    题目描述 给出一个整数n(n<10^30)和k个变换规则(k≤15). 规则: 一位数可变换成另一个一位数: 规则的右部不能为零. 例如:n=234.有规则(k=2): 2->53-> ...

  6. hdu4762Cut the Cake(概率+大数操作(java)+C++高精度模板)

    题目链接:点击打开链接 题目描写叙述:现有一个大蛋糕.上面随机分布了n个草莓,然后将草莓切成m块,问n个草莓全在一块蛋糕上面的概率? 解题思路:细致分析可得:C(n,1)/m^(n-1) 因为m< ...

  7. now code寒假练习赛2——处女座的砝码(找规律题+高精度题)

    #include <bits/stdc++.h> #define ll long long using namespace std; int main() { long double n ...

  8. 【[Offer收割]编程练习赛13 B】最大子矩阵(别人的思路)

    [题目链接]:http://hihocoder.com/problemset/problem/1502 [题意] [题解] 枚举矩形的最上面的行数和最下面的行数(i,j且i<=j); 然后一个变 ...

  9. 【[Offer收割]编程练习赛13 B】最大子矩阵(自己的思路)

    [题目链接]:http://hihocoder.com/contest/offers13/problem/2 [题意] [题解] 算出1..250*250这些数字每个数字的所有因子(成对的那种,即x* ...

随机推荐

  1. linux常用符号命令

    1.符号: 在linux中,&和&&,|和||介绍如下: & 表示任务在后台执行,如要在后台运行redis-server,则有 redis-server & & ...

  2. SEC6 - MySQL 查询语句--------------进阶2:条件查询

    # 进阶2:条件查询 /* 语法: select 查询列表 from 表名 where 筛选条件; 分类: 一.按照条件表达式筛选 条件运算符:> < = !=(等价于<>) ...

  3. form-control的作用

    表单控件加上类form-control后,效果为: 宽度为100% 设置边框为浅灰色 控件具有4px的圆角 设置阴影效果,元素得到焦点时,阴影和边框效果会发生变化 设置placeholder的颜色为# ...

  4. Spring cloud 注册服务小结

    服务注册中心:Eureka.Zookeeper.Cousul.Nacos 使用RestTemplate.openFeign做服务调用,底层使用的是Ribbon. Ribbon做了负载均衡,也可以做一个 ...

  5. 2019 Multi-University Training Contest 1 - 1009 - String - 贪心

    不知道错在哪里. 是要把atop改成stop!两个弄混了.感谢自造样例. #include<bits/stdc++.h> using namespace std; typedef long ...

  6. Codeforces - 1191E - Tokitsukaze and Duel - 博弈论 - 尺取

    https://codeforc.es/contest/1191/problem/E 参考自:http://www.mamicode.com/info-detail-2726030.html 和官方题 ...

  7. Mint-Linux【最佳】【快速】安装微信、企业微信、TIM、QQ等软件

    废话不多说 直接上教程 注意看 方式一.在线安装 在本地目录下.如 /home/root/Document 直接使用在线安装脚本,安装最新的Release版本: wget -qO- https://r ...

  8. 全文搜索引擎 elasticsearch.net

    原文:https://www.cnblogs.com/lonelyxmas/p/10767436.html

  9. windows与linux安装Python虚拟环境

    我这里觉得还是一步到位用virtualenvwrapper  工具,不再讲述virtualenv了,有了工具很好用 windows : 首先安装工具 pip install virtualenvwra ...

  10. elasticsearch 深入 —— Top Hits Aggregation

    Top Hits Aggregation top_hits指标聚合器跟踪正在聚合的最相关文档. 此聚合器旨在用作子聚合器,以便可以按桶聚合最匹配的文档. top_hits聚合器可以有效地用于通过桶聚合 ...