2022icpc昆明补题,K,D,F

目录

K King of Gamers

D Divisions

F Find the Maximum


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个点时,原始等于

         \frac{-n*x^{2}+btot*x}{n}

用bi代表选的点的权值的平均值

              -x^{2}+bi*x

此二次函数最大值在bi/2时取得,答案为

                      {\frac{bi^{2}}{4}}

 我们知道,最终答案转化为选出一个简单路径,使得集合中的点的平均值的绝对值最大(负数也要考虑)

首先经过一番推导我们发现,这个最优集合只有两个点和三个点的情况

因为如果现在的集合是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;
}

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值