题目大意:

  给 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. Web安全测试——常见的威胁攻防

    SQL注入 部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患.用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL In ...

  2. Hugo - 安装、设置及使用

    Hugo 官方主页:https://gohugo.io 待选主题: https://github.com/cdipaolo/gindoro https://github.com/oserz/hugo- ...

  3. Vue Cli 3:创建项目

    一 简介 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,有几个独立的部分. 1 CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令.(vue ...

  4. 【ABAP系列】SAP ABAP常用函数总结第一篇

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP常用函数总结第一 ...

  5. C++ bitset的使用

    bitset 一般代替 bool 数组使用,常用于优化空间,因为 bitset 中一个元素只占 1 bit. bitset 的大小在定义使就需要确定.如果想要不定长的 bitset,就需要使用 vec ...

  6. Java8 Nashorn JavaScript引擎

    使用Java8,Nashorn大大提高了JavaScript 引擎引入,以取代现有的Nashorn Java脚本引擎.Nashorn提供2至10倍更好的性能,因为它直接编译代码在存储器,并传递到字节码 ...

  7. 《JAVA设计模式》之适配器模式(Adapter)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述适配器(Adapter)模式的: 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能 ...

  8. Numpy的基础使用

    数据分析: 是把隐藏在一些看似杂乱无章的数据背后的信息提取出来,总结出所研究对象的内在规律 数据分析的三剑客: Numpy, Pandas, Matplotlib NumPy(Numerical Py ...

  9. 11、numpy——字符串函数

    NumPy 字符串函数 以下函数用于对 dtype 为 numpy.string_ 或 numpy.unicode_ 的数组执行向量化字符串操作. 它们基于 Python 内置库中的标准字符串函数. ...

  10. XC6SLX45T-2FGG484C 原厂订购 原装正品

    作为一家科研公司,保证的原厂品质和正规采购渠道是科学严谨的研发工作中重要的一环,更是保证研发产品可靠.稳定的基础.而研发中所遇到的各种不可预测的情况更是每个工程师向技术的山峰攀登中时会遇到的各种难题. ...