目录
K King of Gamers
题意为 给定一个场数A,给定一个比例a/b 从0开始,每当胜率小于等于a/b,这场必赢,否则必输,问n场中能赢多少场
这道题暴力非常好写,但输入有1e9,很明显是个打表题
单看样例我们很容易被误导直接n*a/b 这也造成了开场时榜上一片红海,,,,
而我们仔细观看打出的表发现
当a,b为3 5时
左为n,中间为暴力打表,右边为n*a/b
当n为86 ,91, 96,98时,与我们直接冲出来的答案不符
经过观察我们发现,除去整除外,右边每一行上取整之后可以和暴力打表吻合
所以答案为n减一之后,输出n*a/b+1
#include <bits/stdc++.h>
#include <iomanip>
#define int long long
#define pb push_back
#define fer(i,a,b) for(int i=a;i<=b;++i)
#define der(i,a,b) for(int i=a;i>=b;--i)
#define all(x) (x).begin(),(x).end()
#define pll pair<int,int>
#define x first
#define y second
#define ld int
using namespace std;
const int N= 1e6+10;
signed main()
{
int T;
cin>>T;
while(T--){
int n,a,b;
cin>>n>>a>>b;
n--;
int res=n*a/b;
cout<<res+1<<endl;
}
}
D Divisions
题意规定一种完美分配方法,把一个序列分成A,B两个序列,A序列不下降,B序列不上升,AB可以为空,数字要按在原序列的顺序插入
让我们构造一个序列,把这个序列分为完美的方法数量为k,输出任意构造的序列,序列中个数不超过365,如果没有此序列则输出-1(懵人的,那有什么-1)
首先经过思考,如果一个序列中有n个一种数字,比如是1,他划分的个数为2的n次方
比如有两个1,他分配方式为4种,为
(注意,A为不上升,B为不下降)
这样我们可以凑出k为2的n次方时的答案
而我们发现当插入一个比1大的数时,比如说2
当插入第一个2时,我们发现以上四种只有第一种可以AB两个序列都插入,其他都只有或A或B插入,所以答案会加1
所以1 1 2的答案为5
诶那可不可以通过加2来实现刚才用1来实现2的n次方一样呢?
经过一番在草纸上推到,我们发现,在1上加2时,第一个2会使答案加1,第二个2会使答案加2,第三个会加4,,,累计加2的n次方-1
这里是一个拼凑的思想,通过无数个2的n次方-1可以拼出任何数
我们只有把2的n次方存到一个集合里,在把他的前缀和存到一个集合里,然后每次lower_bound出补进去的数,即可得到答案
比如要拼个87,我们可以用6个1,四个2,三个3,一个4
总共是2的6次方+2的四次方-1+2的三次方-1+1
正好等于87
还有一个小点就是当输入0时,是有解的,当序列为3 4 1 2时,我们永远不可能把他完美的分割出来
最终答案如下
#include <bits/stdc++.h>
#include <iomanip>
#define int long long
#define pb push_back
#define fer(i,a,b) for(int i=a;i<=b;++i)
#define der(i,a,b) for(int i=a;i>=b;--i)
#define all(x) (x).begin(),(x).end()
#define pll pair<int,int>
#define x first
#define y second
using namespace std;
const int N= 1e6+10;
vector<int> v;
vector<int> s;
signed main()
{
int res=2;
fer(i,1,63){
v.pb(res);
res*=2;
if(res>=1e9){
break;
}
}
s.pb(1);
for(int i=0;i<v.size();++i){
s.pb(v[i]+s.back());
}
int n;
cin>>n;
if(n==0){
cout<<4<<endl;
cout<<"3 4 1 2"<<endl;
return 0;
}
if(n==1){
cout<<6<<endl;
cout<<1<<" "<<1<<" "<<4<<" "<<5<<" "<<1<<" "<<4<<endl;
return 0;
}
auto t=lower_bound(v.begin(),v.end(),n)-v.begin();
if(v[t]==n){
cout<<t+1<<endl;
fer(i,1,t+1){
cout<<1<<" ";
}
cout<<endl;
}
else{
int ji=0;
ji+=t;
vector<int> ans;
ans.pb(t);
int total=v[t-1];
while(total<n){
if(n-total==1){
ji+=1;
ans.pb(1);
break;
}
int bu=n-total;
auto y=lower_bound(s.begin(),s.end(),bu);
if(*y==bu){
ans.pb((int)(y-s.begin()+1));
ji+=(int)(y-s.begin()+1);
break;
}
y--;
ans.pb((int)(y-s.begin()+1));
ji+=((int)(y-s.begin()+1));
total+=*y;
}
cout<<ji<<endl;
for(int i=0;i<ans.size();++i){
fer(j,1,ans[i]){
cout<<1+i<<" ";
}
}
cout<<endl;
}
}
F Find the Maximum
题意为给定一个树,给定每个点的权值(在-1e5到1e5之间),让我们找到一个简单路径(每个点只经过一次),在选一个实数x,使得给定公式的值最大,V是集合中点的数量,bu为权值
首先我们化简一下这个公式,当取n个点时,原始等于
用bi代表选的点的权值的平均值
此二次函数最大值在bi/2时取得,答案为
我们知道,最终答案转化为选出一个简单路径,使得集合中的点的平均值的绝对值最大(负数也要考虑)
首先经过一番推导我们发现,这个最优集合只有两个点和三个点的情况
因为如果现在的集合是9,1,第三个点是9,就可以加进来,变成9 1 9,而想加第四个贡献大的点(10)时,我们就可以割掉前面的9 和 1,得到一个新的最优解 9 10
所以我们只要对每个点的临点进行排序,取最大两个和最小的两个(如果是负数时取)进行计算,更新答案即可
代码如下
#include <bits/stdc++.h>
#include <iomanip>
#define int long long
#define pb push_back
#define fer(i,a,b) for(int i=a;i<=b;++i)
#define der(i,a,b) for(int i=a;i>=b;--i)
#define all(x) (x).begin(),(x).end()
#define pll pair<int,int>
#define x first
#define y second
#define ld long double
using namespace std;
const int N= 1e6+10;
vector<int> v[N];
int w[N];
signed main()
{
int n;
scanf("%lld",&n);
fer(i,1,n) scanf("%lld",&w[i]);
int x,y;
fer(i,1,n-1)
{
cin>>x>>y;
v[x].push_back(w[y]);
v[y].push_back(w[x]);
}
ld res=-1e9;
fer(i,1,n)
{
sort(v[i].begin(),v[i].end());
ld L=0;
ld R=(1.0*w[i]+v[i][v[i].size()-1])/2;
if(v[i].size()>=2){
L=(1.0*w[i]+v[i][v[i].size()-1]+v[i][v[i].size()-2])/3;
}
res=max(res,L);
res=max(res,R);
}
fer(i,1,n)
{
ld L=0;
ld R=(1.0*w[i]+v[i][0])/2;
if(v[i].size()>=2){
L=(1.0*w[i]+v[i][0]+v[i][1])/3;
}
res=max(res,-L);
res=max(res,-R);
}
res=res*res/4;
cout<<fixed<<setprecision(6)<<res<<endl;
}