题目大意:

  给 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. 非典型T_SQL的总结

      ------over的两种常用的用法--- --第一种分组 当然要注意了,这里的分组并不是实际的分组,而是根据你的业务需求而坐的临时分组   select roomguid,Room, avg(t ...

  2. 从有状态应用(Session)到无状态应用(JWT),以及 SSO 和 OAuth2

    不管用哪种方式认证用户,都可能被中间人攻击窃取 SessionID 或 Token,从而发生 CSRF 攻击.解决方式就是全站 HTTPS.现在 Let's Encrypt 已经支持免费的通配符 HT ...

  3. 《STL源码剖析》——第七、八章:仿函数与接配器

    第七章:仿函数  7.1.仿函数(函数对象)概观 STL仿函数的分类,若以操作数(operand)的个数划分,可分为一元和二元仿函数,若以功能划分,可分为算术运算(Arithmetic).关系运算(R ...

  4. Reinforcement Learning for Self Organization and Power Control of Two-Tier Heterogeneous Networks

    R. Amiri, M. A. Almasi, J. G. Andrews and H. Mehrpouyan, "Reinforcement Learning for Self Organ ...

  5. Spring Security 01

    环境搭建 maven依赖jar包 <!-- spring-security --> <dependency> <groupId>org.springframewor ...

  6. input限制小数点的位数

    在做限制input小数点的时候,我本来想通过vue里面的的watch监听来实现, ---实现逻辑是,通过监听输入的内容,当出现"."(点)的时候,记录通过indexOf获取点的位置 ...

  7. python开发之路-day01

    1.Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为A ...

  8. spring中bean的构造函数,Autowired(Value)注入与@PostConstruct调用顺序

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/yyysylvia/article/deta ...

  9. 【转】Linux下vim的基本操作

    原文链接 Linux vi/vim 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能 ...

  10. NGUI技能CD效果制作(sprite的type:filled)

    一,我们先添加一个sprite,改名为skill.给当前skill添加图片,然后再sprite下添加一个sprite和一个label,结果如下 二现在我们来设置skill下的sprite,给他设置一个 ...