题目大意:

  给 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. nginx配置多个虚拟主机(mac)

    1 . 安装  通过homebrew安装nginx,默认安装在:/usr/local/Cellar/nginx/版本号.配置文件在路径:/usr/local/etc/nginx ,默认配置文件ngin ...

  2. JS基础(上)

    JS与DOM的关系 浏览器有渲染html代码的功能,把html源码(如div,p标签等)在内存里形成一个DOM对象 文档对象模型DOM(Document Object Model)定义访问和处理HTM ...

  3. C#联接序列

    1.  Concat() – 串联序列(有重复项) var healthFoods = new List<string> { "fruits", "veget ...

  4. eclipse或者myeclipse的代码提示功能

    第一步:打开eclipse,在菜单栏选择window-->preferences 第二步:在左边tree菜单栏选择General-->keys 第三步:在右边的文本框中输入content, ...

  5. JPA Example查询

    //创建查询条件数据对象 Customer customer = new Customer(); customer.setAddress("河南省郑州市"); customer.s ...

  6. 洛谷 P1440 求m区间内的最小值(单调队列)

    题目链接 https://www.luogu.org/problemnew/show/P1440 显然是一道单调队列题目…… 解题思路 对于单调队列不明白的请看这一篇博客:https://www.cn ...

  7. emacs配置&博客界面源代码

    emacs配置 如果想要考场简单配置也可以去下面看,需要别的考场配置可以自己在下面比较全的里面找 考试备忘录(有新的就会更的...) By Junlier (global-set-key [f9] ' ...

  8. 理解Throughput和Latency

    Throughput,中文译作吞吐量.Latency,中文译作延迟.它们是衡量软件系统的最常见的两个指标. 吞吐量一般指相当一段时间内测量出来的系统单位时间处理的任务数或事务数(TPS).注意“相当一 ...

  9. GitHub 风格的 Markdown 语法

    GitHub 风格的 Markdown 语法 [译] GitHub 风格的 Markdown 语法 Original: GitHub Flavored Markdown - GitHub Help T ...

  10. [三下五除二]在Eclipse上的JFinal_Demo

    承接上回在IDEA的JFinal的项目的导入,今次同样是同一个文件,但在Eclipse上运行.在Eclipse上运行官网的JFinal的例子是及其快捷. 打开Eclipse,并进入如下的界面. 点击导 ...