APIO2019 练习赛 Wedding cake——思路+高精度
题目大意:
给 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——思路+高精度的更多相关文章
- HDU 4762 Cut the Cake(高精度)
Cut the Cake Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tota ...
- HDU4762 Cut the Cake
HDU4762 Cut the Cake 思路:公式:n/m(n-1) //package acm; import java.awt.Container; import java.awt.geom.A ...
- Hdoj 2289.Cup 题解
Problem Description The WHU ACM Team has a big cup, with which every member drinks water. Now, we kn ...
- 3D Food Printing【3D食物打印】
3D Food Printing There's new frontier in 3D printing that's begining to come into focus: food. 3D打印的 ...
- 洛谷 P1037 产生数
题目描述 给出一个整数n(n<10^30)和k个变换规则(k≤15). 规则: 一位数可变换成另一个一位数: 规则的右部不能为零. 例如:n=234.有规则(k=2): 2->53-> ...
- hdu4762Cut the Cake(概率+大数操作(java)+C++高精度模板)
题目链接:点击打开链接 题目描写叙述:现有一个大蛋糕.上面随机分布了n个草莓,然后将草莓切成m块,问n个草莓全在一块蛋糕上面的概率? 解题思路:细致分析可得:C(n,1)/m^(n-1) 因为m< ...
- now code寒假练习赛2——处女座的砝码(找规律题+高精度题)
#include <bits/stdc++.h> #define ll long long using namespace std; int main() { long double n ...
- 【[Offer收割]编程练习赛13 B】最大子矩阵(别人的思路)
[题目链接]:http://hihocoder.com/problemset/problem/1502 [题意] [题解] 枚举矩形的最上面的行数和最下面的行数(i,j且i<=j); 然后一个变 ...
- 【[Offer收割]编程练习赛13 B】最大子矩阵(自己的思路)
[题目链接]:http://hihocoder.com/contest/offers13/problem/2 [题意] [题解] 算出1..250*250这些数字每个数字的所有因子(成对的那种,即x* ...
随机推荐
- Oracle 11g修改默认端口1521为其他值
1.修改listener.ora 打开文件D:\app\Administrator\product\11.2.0\dbhome_1\NETWORK\ADMIN\listener.ora,修改PORT ...
- Vagrant 手册之网络 - 概述及基本用法
原文地址 - 概述 原文地址 - 基本用法 为了访问创建的 Vagrant 环境,Vagrant 为端口转发.连接公共网络.创建私有网络等功能暴露了一些高层网络选项. 高层网络选项用于提供可以跨 pr ...
- C#通过UserAgent判断智能设备(Android,IOS,PC,Mac)
尝试通过 Agent 来获取相应的智能手机设备标识,根据标识的不同来输出对应设备所需的显示样式及其他.经过努力,终于搜集了比较全的 智能设备 的 Agent,相应的判断过程及代码如下,不明白的留言. ...
- Mac004--Tomcat安装
Mac--Tomcat安装 一.Tomcat下载 https://tomcat.apache.org/download-80.cgi 下载Tomcat注意tomcat与jdk版本要一致.jdk1.8版 ...
- an安装jenkins时遇到ERROR: No Java executable found in current PATH: /bin:/usr/bin:/sbin:/usr/sbin的问题
# sudo /etc/init.d/jenkins restartERROR: No Java executable found in current PATH: /bin:/usr/bin:/sb ...
- [Bzoj1008][HNOI2008]越狱(组合计数)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1008 组合计数的简单题,可能越狱的方案数等于总方案数-不可能越狱的方案数,则: 总方案数 ...
- memset 初始化数组 & 实现原理
初始化数组可不必使用n重for循环. 原理 memset具有初始化数组的功能,能够初始化数组中的每一个值. 它是将数组中的每一个数的二进制的每一个字节初始化的. 比如初始化int类型的a数组:mems ...
- [洛谷P1552] [APIO2012]派遣(左偏树)
这道题是我做的左偏树的入门题,奈何还是看了zsy大佬的题解才能过,唉,我太弱了. 左偏树总结 Part 1 理解题目 很显然,通过管理关系的不断连边,最后连出来的肯定是一棵树,那么不难得出,当一个忍者 ...
- vmware 15安装centos 7.6
环境: Vmware 15 centos 7.6.1810(CentOS-7-x86_64-DVD-1810.iso) centos:centos是linux发行版之一,开源,免费,安装的iso可以向 ...
- SLA服务可用性4个9是什么意思?怎么达到?
SLA:服务等级协议(简称:SLA,全称:service level agreement).是在一定开销下为保障服务的性能和可用性,服务提供商与用户间定义的一种双方认可的协定.通常这个开销是驱动提供服 ...