UPC近期比赛题目

问题 D: 考试

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
S中开展了省选集训,有n位选手的实力参差不齐。众所周知,如果题目太水,那么就会有人AK离场后打游戏,如果题目太难,那么就会有人颓废离场后打游戏。作为出题人的你自然不希望有太多人出去打游戏,不然ob就会很生气。所以你需要设定题目的难度,尽量让最少的人出去打游戏,并且同时题目尽可能难一些。
输入
第1行一个正整数n,表示一共有n名选手参与;
接下来n行两个非负整数Ai,Bi,表示第i位选手可接受的题目难度范围在Ai~Bi之间,若你设定的题目难度超过此区间,这位选手便会离场打游戏。

输出
一共一行一个数字ans,表示你设定的题目难度为ans,应当在让最少的人离场的同时使其尽可能的大。
样例输入 Copy
3
1 5
95 105
5 110
样例输出 Copy
105
提示
样例解释
有三名选手参与,方便起见命名第1名选手叫小X,第2名选手叫小Q,第3名选手叫小P。
题目难度如果要满足蒟蒻小X,那么小Q便一定会AK后颓废,为了兼顾小P,难度不得不设为5,此时1人离场。
如果要满足巨佬小Q,那么无论如何小X也会觉得题目太难愤愤离场,为了兼顾小P,难度最大可设为105,此时1人离场。
综上,题目难度最大为105。
【数据范围】
对于30%的数据:n ≤ 10
对于50%的数据:n ≤ 1000
对于另外20%的数据: 0 ≤ Ai ≤ Bi ≤ 100000
对于100%的数据: n ≤ 100000, 0 ≤ Ai, Bi ≤ 1000000000

思路:差分+离散化
先是输入左端右端,
lower_bound 二分查找

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
const int mod=998244353;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
 
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str,s;
struct node {
    ll l,r;
}q[maxn];
bool cmp(node a,node b){
    return a.r<b.r;
}
 
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&q[i].l,&q[i].r);
        q[i].r++;
        b[++cnt]=q[i].l;
        b[++cnt]=q[i].r;
     }
     sort(b+1,b+1+cnt);
    ans=unique(b+1,b+1+cnt)-b-1;
    for(int i=1;i<=n;i++)
    {
        q[i].l=lower_bound(b+1,b+1+ans,q[i].l)-b;
        q[i].r=lower_bound(b+1,b+1+ans,q[i].r)-b;
        dis[q[i].l]++;
        dis[q[i].r]--;  
    }
    for(int i=1;i<=ans;i++){
        sum+=dis[i];
        if(sum>=minn){
        res=max(b[i+1]-1,res);
        minn=sum;
        }
    }
    cout<<res<<endl;
    return 0;
}


问题 A: 结队

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
众所周知,小X是一个风流倜傥、有极高人格魅力的奆老。
奆老总喜欢挑战高难度的事情,幻想着横穿和自己一样大名鼎鼎的亚马逊雨林。
这个幻想了7083年的事情终要变为现实,小X高兴得连灌两盆洗脚水。
“啊……真香……该死的甜美…………”

古代著名学者度娘在《百度百科》一书中说到:
亚马逊热带雨林(AmazonRainForest)位于南美洲的亚马逊平原,占地550万平方公里。雨林横越了9个国家:巴西(占森林60%面积)、哥伦比亚、秘鲁、委内瑞拉、厄瓜多尔、玻利维亚、圭亚那、苏里南以及法国(法属圭亚那),占据了世界雨林面积的一半,占全球森林面积的20%,是全球最大及物种最多的热带雨林。

小X万年一遇地被吓尿了。他感到面临着生命危险,需要小弟来保护,而自己的小弟实在是太少了,小X灵机一动,想到JSOI的夏令营中有很多dalao。小X照照镜子,被自己的人格魅力深深折服,信心满满。
[9102年8月8日,江苏省常州高级中学]
刚下车,小X身旁就围了一大群猛男。
“小X最帅!”
没有想到的是,小X居然被秀了。省中的dalao早已准备在这一天组成帮派。

省中的dalao从1~n进行编号。小X口味独特,只想让[a,b]区间内的dalao做自己的小弟。
dalao遵循以下规则组成帮派:
最初每个人都自成帮派。组帮派时,对于序号为m,n两个属于不同帮派的dalao,如果m,n有大于等于p的公共质因数,那么m,n所在的帮派就会合并为同一帮派。合并过后的帮派会继续合并,直至没有可以合并的帮派。
小X作为一个奆老,秉承着装弱的传统。他需要你告诉他,一共会有多少不同的帮派。

输入
一行,三个正整数A,B,P。(保证A≤B)
输出
一个正整数,表示最终的帮派个数。
样例输入 Copy
10 20 3
样例输出 Copy
7
提示
样例解释
对于[10,20]中的dalao,可分成如下帮派:
{10,20,12,15,18} {11} {13} {14} {16} {17} {19}
【数据范围】
对于30%的数据,保证有0≤A≤B≤100,P≤100
对于60%的数据,保证有0≤A≤B≤3000,P≤3000
对于100%的数据,保证有0≤A≤B≤50000,P≤50000

思路:”并查集+数学
因为他要分堆 就是一堆一堆 这一堆中两个数的最大质因数大于要求的值就放在一起,就是把这两个数连在一起。所以需要用并查集。
还有一点就是 最大质因数。 这个不一定是最大公约数,因为如果是合数他就不是结果,所以要分解一遍这个数,分解成质数;唯一分解定理就OK了。小于p 的数 约数一定会小于p不用考虑,一律单独放。

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
const int mod=998244353;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str,s;
 
vector <ll>v[maxn];
 
ll f(ll x) {
    if(dis[x]==x) return x;
    else return dis[x]=f(dis[x]);
}
void so(ll x){
    ll y=x;
    for(int i=2;i*i<=x;i++){
        if(x%i==0){
            v[i].push_back(y);
            while(x%i==0) x/=i;
        }   
    }
    if(x>1) v[x].push_back(y);
}
 
int main(){
    cin>>n>>m>>p;
    for(int i=1;i<=m;i++)
        dis[i]=i;
    for(int i=n;i<=m;i++)
        so(i);
         
    for(int i=m;i>=p;i--){
        l=v[i].size();
        if(l==0) continue;
        ans=f(v[i][0]);
        for(int j=1;j<l;j++)
        {
            cnt=f(v[i][j]);
            dis[cnt]=ans;
         }
     }
     for(int i=n;i<=m;i++)
        if(dis[i]==i) sum++;
    cout<<sum<<endl;
    return 0;
}


问题 B: 砝码

时间限制: 1 Sec 内存限制: 256 MB
[提交] [状态]
题目描述
FJ有一架用来称牛的体重的天平。与之配套的是N(1<=N<=1000)个已知质量的砝码(所有砝码质量的数值都在31位二进制内)。每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(FJ不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当FJ把砝码放到她的蹄子底下,她就会尝试把砝码踢到FJ脸上)。天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于C(1<=C<2^30)时,天平就会被损坏。
砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。
FJ想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。
由于天平的最大承重能力为C,他不能把所有砝码都放到天平上。
现在FJ告诉你每个砝码的质量,以及天平能承受的最大质量。你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。
输入
第1行: 两个用空格隔开的正整数,N和C。
第2…N+1行: 每一行仅包含一个正整数,即某个砝码的质量。保证这些砝码的质量是一个不下降序列。
输出
一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量。
样例输入 Copy
3 15
1
10
20
样例输出 Copy
11
提示
FJ有3个砝码,质量分别为1,10,20个单位。他的天平最多只能承受质量为15个单位的物体。用质量为1和10的两个砝码可以称出质量为11的牛。这3个砝码所能组成的其他的质量不是比11小就是会压坏天平。

思路:直接暴力从后向前枚举,用前缀和优化。
优化:

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
const int mod=998244353;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str,s;
 
void bfs(ll x,ll sum){
    if(sum>m||dis[x-1]+sum<=ans){
        return ;
    }
     
     
    ans=max(sum,ans);
    for(int i=x-1;i>=1;i--){
        sum+=a[i];bfs(i,sum);
        sum-=a[i];
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        dis[i]=dis[i-1]+a[i];
    }
    bfs(n+1,0);
    cout<<ans<<endl;
    return 0;
}


问题 D: 数字变换

时间限制: 2 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
给定一个数N (O≤N≤100000),变成另一个数K(O≤K≤100000),允许的操作是乘以2,或者加减1,问最少要几步才能完成?
输入
仅有两个整数 N 和 K。
输出
一个整数,表示需要的最少步数。
样例输入 Copy
5 17
样例输出 Copy
4

思路 :bfs+队列
两种情况(也可以不分析):就是一种是N>K 只能减
另一种就是 用bfs来解决 每一步 有3种情况 +1 ,-1,*2,
两个数组 一个标记(vis)是否走过,另一个(dis)记录到这个数的步数。上边界是100000 下边界就是0;当得到结果是直接返回dis中的值就行。
代码:

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
const int mod=998244353;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str,s;
queue<ll> q;
ll bfs(ll n,ll m){
    ll h,next;
    q.push(n);
    dis[n]=0;
    vis[n]=1;
    while(!q.empty()){
        h=q.front();//进行第一个数的深搜
        q.pop();//把第一个数pop掉
        for(int i=0;i<3;i++){
            if(i==0) next=h-1;
            else if(i==1) next=h+1;
            else next=h*2;
            if(next<0||next>=maxn) continue;//上下限
            if(!vis[next]){//没到过进行 到过退出
                q.push(next);//入队
                dis[next]=dis[h]+1;//步数
                vis[next]=1;//标记
            }
            if(next ==m) return dis[next];//第一个返回的值一定是最少步数到达。
        }
    }
}
 
int main(){
    cin>>n>>m;
    if(n>=m) cout<<n-m<<endl;
    else cout<<bfs(n,m)<<endl;
    return 0;
}


问题 E: 最佳课题选择

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]

题目描述

Matrix67要在下个月交给老师n篇论文,论文的内容可以从m个课题中选择。由于课题数有限,Matrix67不得不重复选择一些课题。完成不同课题的论文所花的时间不同。具体地说,对于某个课题i,若Matrix67计划一共写x篇论文,则完成该课题的论文总共需要花费Ai*x^Bi个单位时间(系数Ai和指数Bi均为正整数)。给定与每一个课题相对应的Ai和Bi的值,请帮助Matrix67计算出如何选择论文的课题使得他可以花费最少的时间完成这n篇论文。

输入

第一行有两个用空格隔开的正整数n和m,分别代表需要完成的论文数和可供选择的课题数。
以下m行每行有两个用空格隔开的正整数。其中,第i行的两个数分别代表与第i个课题相对应的时间系数Ai和指数Bi。

输出

输出完成n篇论文所需要耗费的最少时间。

样例输入 Copy

10 3
2 1
1 2
2 1

样例输出 Copy

19

提示

样例解释
4篇论文选择课题一,5篇论文选择课题三,剩下一篇论文选择课题二,总耗时为241+112+2*5^1=8+1+10=19。可以证明,不存在更优的方案使耗时小于19。

对于100%的数据,n<=200,m<=20,Ai<=100,Bi<=5。

思路:这个题就是对背包DP的理解,边输入边存放

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
const int mod=998244353;
const int MOD=10007;

inline int read() {
	int x=0;
	bool t=false;
	char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}

priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆 		小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下   	大根堆  	大到小
map<ll,ll>mp;

ll n,m,t,l,r,p;
ll sum=1e15,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str,s;

ll qpow(ll a,ll b) {
	ll sum=1;
	while(b) {
		if(b&1) sum=sum*a;
		b>>=1;
		a=a*a;
	}
	return sum;
}

int main(){
 	cin>>n>>m;
	memset(dis,inf,sizeof(dis));
	dis[0]=0;
	
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&a[i],&b[i]);
		for(int j=1;j<=n;j++)
			vis[j]=a[i]*qpow(j,b[i]);
		for(int j=n;j>=1;j--)
			for(int k=1;k<=j;k++)
				dis[j]=min(dis[j],dis[j-k]+vis[k]);
	}
	

	cout<<dis[n]<<endl;
	
	return 0;
}




问题 A: 地球发动机

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
“啊,地球,我的流浪地球……”——《流浪地球》
在一条直线上,从左到右排列着n台地球发动机,每台发动机有着固定的位置坐标Ai和功率Pi,保证Ai<Ai+1。此外,由于地球发动机的特性,每台发动机还有一个参数Xi,如果一台发动机运行,则坐标范围在[Ai,Ai+Xi]的其它发动机就无法运行。现在你想让正在运行的发动机总功率最大,请输出这个总功率。

输入
第一行一个整数n,意义如上所述。
接下来n行,每行三个整数Ai,Pi,Xi,意义如题面所述。

输出
一行一个整数,表示可能的最大功率。
样例输入 Copy
4
2 5 1
5 4 3
8 10 3
9 2 2
样例输出 Copy
15
提示
对于20%的数据,n≤10,0<Ai,Pi,Xi≤10;
对于50%的数据,n≤2000,0<Ai,Pi,Xi≤105;
对于100%的数据,n≤105,0<Ai,Pi,Xi≤109。

思路:开一个数组存放对应功率最大;直接从后想前查询 因为upper_bound返回的是比他大的下标的 如从前往后的话后面还没遍历所以不知道后面的情形,所以从后往前。

代码:

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1e5;
#define inf 0x3f3f3f3f
const int mod=1000000007;
const int MOD=10007;

inline int read() {
	int x=0;
	bool t=false;
	char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}

priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆 		小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下   	大根堆  	大到小


ll n,m,t,l,r;
ll cnt,flag,maxx,minn;
ll dis[maxn],vis[maxn];
ll dp[1100][1100];
ll f[maxn];
ll a[maxn],p[maxn],x[maxn];

int main() 
{

	ll i,pos;
	scanf("%lld",&n);
	for(i=1;i<=n;++i)
	  scanf("%lld%lld%lld",&a[i],&p[i],&x[i]);
	for(i=n;i;--i)
	{
		pos=upper_bound(a+1,a+n+1,a[i]+x[i])-a;
		f[i]=max(f[i+1],f[pos]+p[i]);
		maxx=max(maxx,f[i]);
	}
	printf("%lld",maxx);

	return 0;
}

拓展

lower_bound和upper_bound的用法

#include<iostream>
using namespace std;
int lower_bound(int *a, int n, int value) //二分查找第一个大于或等于value的下标key
{
	int l = 0;
	int r = n - 1;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (a[mid] < value)
			l = mid + 1;
		else
			r = mid - 1;
	}
	return l;
}
int upper_bound(int *a, int n, int value) // 返回第一个大于value的key
{
	int l = 0;
	int r = n - 1;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (a[mid] <= value)
			l = mid + 1;
		else
			r = mid - 1;
	}
	return l;
}
int main()
{
	int input[20] = {0, 2, 3, 4, 4, 5, 5, 7, 8, 9};
	int key1,key2;
	key1 = lower_bound(input, 10, 4);
	key2 = upper_bound(input, 10, 5);
	cout << key1 << "\t" << key2 << endl;
	return 0;
}



问题 C: SLF改造计划

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
俗话说得好,精卫填海,LF平山。作为处女座的SLF强迫症有很多,他学成后买下一个荒无人烟的山丘地带,但是山的高度很是令他烦恼,于是他决定要用最小的代价让最高的山峰与最低的山峰的高度差不超过17,SLF经过调查,已知第i座山峰高度为a[i],由于填山或是平山都需要代价,SLF询问了专业人员,将高度为a[i]的山峰改造成高度为x的山峰的代价为(a[i]–x)2,经过苦难的他深知赚钱的不容易,所以他希望代价最小。
输入
第一行一个数n。
接下来n行每行一个整数a[i](0≤a[i]≤100),表示山峰高度。
输出
一行一个整数,最小的代价
样例输入 Copy
5
20
4
1
24
21
样例输出 Copy
18
提示
对于30%的数据,1≤n≤100
对于50%的数据,1≤n≤500
对于100%的数据,1≤n≤1000
思路:
因为数据量比较小 所以可以直接暴力跑 以小于最大值的数做基点开始跑取最小值就行了。
代码:

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
const int mod=998244353;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str[1010][10];
 
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        maxx=max(maxx,a[i]);
    }
    minn=1e9+7;
    sort(a+1,a+1+n);
    for(int i=0;i<=maxx;i++){
        flag=0;
        for(int j=1;j<=n;j++){
            if(a[j]>i) flag+=(a[j]-i)*(a[j]-i);
            else if(i-a[j]>17){
                flag+=(i-a[j]-17)*(i-a[j]-17);
            }
        }
        minn=min(flag,minn);
    }
    cout<<minn<<endl;
    return 0;
}



问题 C: 马拉松比赛

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
明明同学参加马拉松比赛,马拉松全程包括N个检查点(3<=N<=500),明明同学要按顺序通过,检查点1在起始位置、检查点N是终点。明明同学应该通过所有这些检查点,但是他最近比较累,他决定跳过K个检查点(K<N-1)以尽可能缩短总路程。但显然,他不能跳过检查点1或N,因为这太容易被发现了!
请帮助明明同学找到他长跑的最短距离,如果他可以跳过K个检查点的话。
如果两个检查点的位置分别为(x1,y1)和(x2,y2),则它们的距离为|x1-x2|+|y1-y2|。
输入
第一行两个用空格隔开的整数N和K。
接下来的N行每个包含两个空格分隔的整数,x和y,代表一个检查点(-1000<=x<=1000,-1000<=y<=1000)。注意,可能几个检查点会在同一物理位置。明明跳过这样的检查站时,他一次只能跳过一个检查站。
输出
一个正整数,表示他能跑的最短距离。
样例输入 Copy
5 2
0 0
8 3
1 1
10 -5
2 2
样例输出 Copy
4

#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#define X first
#define Y second
using namespace std;
const int maxn=2e6+1010;
 
typedef long long ll;
typedef pair<ll,ll> PII;
 
const int N=510,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
PII p[N];
ll n,m,t,k,l,r;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn],head[maxn];
ll dp[1010][1010];
 
ll get_dis(ll a,ll b)
{
    PII x=p[a],y=p[b];
    return abs(x.X-y.X)+abs(x.Y-y.Y);
}
 
int main()
{
 
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        p[i]={x,y};
    }
     
    memset(dp,0x3f,sizeof dp);
    for(int i=0;i<=n;i++)
        dp[1][i]=0;
         
    for(int i=1;i<=n;i++)//枚举位置 
        for(int j=0;j<=k;j++)//枚举剩余的 
            for(int t=0;t<=j&&i+t+1<=n;t++)//枚举要用的 
                dp[i+t+1][j-t]=min(dp[i+t+1][j-t],dp[i][j]+get_dis(i,i+t+1));
     
    printf("%lld\n",dp[n][0]);
 
 
 
    return 0;  
}
 


问题 D: 最大值

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
给出N个整数,和一个长度值Len,要求在这N个整数中每个长度为Len的连续一段数中的最大值。

例如:N=8,Len=3,8个整数是:2 5 1 1 2 4 7 1。答案是 5 5 2 4 7 7 。
解释:
2 5 1的最大值是5
5 1 1的最大值是5
1 1 2的最大值是2
1 2 4的最大值是4
2 4 7的最大值是7
4 7 1的最大值是7
输入
第一行2个正整数:N,Len。N范围[2…100000],Len范围[2…N]
第二行:N个正整数,每个数范围[1…1000000000]。
输出
一行,N-Len+1个整数。
样例输入 Copy
4 3
7 2 1 4
样例输出 Copy
7 4

思路看第二个代码吧。还是说一下吧:简单来说就是单调栈思想;
用一个数组指区间值的下标(因为要求最大值也就是如果这个数大于这个数组的最后一位的话就替换下来,如果小于就把这个数存放到里面)。
我说的有点不明白参考一下人家说的吧:
插入:为了保证单调队列的递减性,我们在插入元素xx(struct x类型)的时候,要将队尾的元素和xx的a成员(表示该元素的数据)比较,如果队尾的元素不大于a,则删除队尾的元素,然后继续将新的队尾的元素与a比较,直到队尾的元素大于a,这个时候我们才将xx插入到队尾。
删除:由于我们只需要保存len个元素中的最大值,所以当队首的元素的下标小于等于i-len的时候,就说明队首的元素对于求最大值已经没有意义了。(注意不是每经过一个循环后就删除队首元素,而是要根据队首元素的下标来确定,因为有时单调队列中的元素不一定是len个)所以当index<=i-len时,将队首元素删除。

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e6+1010;
#define inf 0x3f3f3f3f
const int mod=1000000007;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn],head[maxn];
ll dp[1010][1010];
 
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    flag=0;
    cnt=-1;
    for(int i=1;i<=m;i++)
    {
        for(;flag<=cnt&&a[i]>=a[dis[cnt]];cnt--);
            dis[++cnt]=i;
    }
    cout<<a[dis[flag]]<<" ";
    for(int i=m+1,j=1;i<=n;i++,j++){
        if(flag<=cnt&&dis[flag]<=j) flag++;
        for(;flag<=cnt&&a[i]>=a[dis[cnt]];cnt--);
        dis[++cnt]=i;
        cout<<a[dis[flag]]<<" ";
    }
     
     
     
     
     
     
     
     
    return 0;
}

代码二:

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e6+1010;
#define inf 0x3f3f3f3f
const int mod=1000000007;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn],head[maxn];
ll dp[1010][1010];
 
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    flag=0;//标记 最大的下标 
    cnt=-1;//
    for(int i=1;i<=m;i++)
    {
        for(;flag<=cnt&&a[i]>=a[dis[cnt]];cnt--);//最后一位变大或者不变 直接换下来 
            dis[++cnt]=i;// 变小 把这个数加进去 
    }
    cout<<a[dis[flag]]<<" ";
    for(int i=m+1,j=1;i<=n;i++,j++){
        if(flag<=cnt&&dis[flag]<=j) flag++;//j来标记是否略过去这个数 
        cout<<"flag"<<" "<<flag<<endl;
        for(;flag<=cnt&&a[i]>=a[dis[cnt]];cnt--);
        dis[++cnt]=i;
        cout<<"dis:";
        for(int i=0;i<=cnt;i++)
			cout<<dis[i]<<" ";
		cout<<endl;
        cout<<"max:"<<a[dis[flag]]<<" ";
        cout<<endl;
    }
    return 0;
}

问题 E: MAX 的读书计划

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
MAX 很喜欢读书,为了安排自己的读书计划,他会预先把要读的内容做好标记,A B 表示一个页段,即第 A 到 B 面,当然 A<B,若有两个页段 A-B,B-C,则可以直接记为 A-C,这样,他就可以一次看完,现在告诉你 n 个页段,请你帮他求出最长的一条页段,并输出这条页段的长度和组成它的页段个数。举个例子:
有 6 个页段:
2-7 1-3 3-12 12-20 7-10 4-50
那么连续的页段就有:
1-3,3-12,12-20 长度为 20-1+1=20 由 3 个页段组成
2-7,7-10 长度为 10-2+1=9 由 2 个页段组成
4-50 长度为 50-4+1=47 由 1 个页段组成
那么最长的一条就是第三个,所以结果为 47 1。
需要注意的是:如果有两条不一样的连续的页段长度同时为最大,那么取组成页段数多的一条.
例子: 1-5,5-10,1-10
输出: 10 2
输入
第一行为一个整数n,n<500;
第二行到第n+1行,每行两个整数A,B,记录一个页段的信息。0<=A<B<500
输出
输出一个整数,即最长的页段的长度和组成它的页段数。
样例输入 Copy
7
1 5
10 12
3 10
2 7
2 10
12 16
7 9
样例输出 Copy
15 3
提示
1-5 长度为5由1个页段组成
3-10,10-12,12-16 长度为14由3个页段组成
2-7,7-9 长度为8由2个页段组成
2-10,10-12,12-16 长度为15由3个页段组成

所以输出最长的页段的长度即15由3个页段组成

【数据规模】
对于30%的数据n<20,0<=A<B<500
对于100%的数据n<500,0<=A<B<500

  • 思路:两个点如果不能直接连通的话需要在她直接找到一个点连通 这个点与第一个点,在联通这个点跟后一个点,所以我们就要在连接点上来看了。先设二维数组,存放长度,且存在。然后就是判断了,如果两点之间有连接点,则判断前一点是否可由他这之前的到达,如果能,判断长度与原本的大小,大则赋值 并标记下来连接点,不能到到直接比较他遇到当前的长度大小。
  • 然后找到最长。然后找桥梁最多的。

代码:

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
const int mod=1e9+7;
const int MOD=10007;
 
inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
 
priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆        小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下    大根堆     大到小
map<ll,ll>mp;
 
ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag
,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str,s;
 
struct king {
    ll id;
    string str;
}q[maxn];
bool cmp(king a,king b){
    if(a.id!=b.id) return a.id>b.id;
    else return a.str<b.str;
}
ll qpow(ll a,ll b){
    ll sum=1;
    while(b){
        if(b&1) sum=sum*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return sum;
}
 
 
int main(){
    cin>>n;
    for(int i=1;i<=n;i ++)
    {
        scanf("%lld%lld",&l,&r);
        dp[l][r]=r-l+1;
        maxx=max(maxx,r);
    }
    for(int i=0;i<=maxx;i++){
        for(int j=0;j<i;j++){
            if(dp[j][i]){//存在  
                if(dis[j]){
                    if(dis[i]<dis[j]+dp[j][i]){
                        dis[i]=dis[j]+dp[j][i]-1;//最大长度 
                        vis[i]=j;//这个的中间连接点是j 也就是前一个点 
                     }
                 }else {//这个连接点不存在 直接用 新的一个点做根 
                    dis[i]=max(dis[i],dp[j][i]);
                 }
             }
         }
     }
     for(int i=0;i<=maxx;i++)
     {
        if(dis[i]>minn)
            minn=dis[i];//找到最长的 
     }
     sum=1;
     for(int i=0;i<=maxx;i++){
        if(dis[i]==minn){
            l=1,r=i;
            while(vis[r]!=0){//由最后一点 向前找 
                l++;
                r=vis[r];
             }
            sum=max(sum,l);
         }
     }
     cout<<minn<<" "<<sum<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45911397/article/details/105688631
UPC