【2019-总结】初中毕业暑假集训No.5

目录

前言

题目解析

一、Tuna

题目

分析

代码

二、Pareto

题目

分析

AC代码-Myself

AC代码-std版

三、Unija

题目

分析

考试瞎搞导致WA-代码

AC代码

四、Ronald

题目

分析

代码

五、Poklon

题目

分析

MLE-考试瞎搞-代码

AC代码


前言

本场考试被同学们(一群大佬!except me!)称为“基础测验”,前4~5道题基本ac(except me!)

所以在大家正欢乐地研究第六道题时,我默默地补起了题并打开了CSDN... ...

总结一下,有两点:

1.自己思路太清奇,没想清楚,有点“赌一把”的心态  

2.基础不好,有待提升,要多刷题并及时写博客督促自己搞懂

题目解析

一、Tuna

题目

Sample Input 1

5 2
3 4
2 1
5 3
4 4
4 2

Sample Output 1

19

Sample Input 2

4
2
3 5
2 8

6 5
6 3
7

Sample Output 2

22

Sample Input 3

3
10
20 50
30
20 40
50
70 20
10

Sample Output 3

90

分析

如果p1,p2差值小于等于x,答案就加较大值

否则输入并加入p3

(差点想复杂了,以为会有abs(p1-p2)<=x且输入了p3的情况)

代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,x,cnt,p1,p2,p3;
int main()
{
	//freopen("tuna.in","r",stdin);
	//freopen("tuna.out","w",stdout);
	scanf("%d%d",&n,&x);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&p1,&p2);
		if(abs(p1-p2)<=x)
			cnt+=max(p1,p2);
		else
		{
			scanf("%d",&p3);
			cnt+=p3;
		}
	}
	printf("%d\n",cnt);
	return 0;
}

二、Pareto

题目

Sample Input 1

2
100 200

Sample Output 1

50.0

66.66666666666666

Sample Input 2


100100 10 100 1000 1 10100 90100
100100

Sample Output 2

37.5

96.28172769816027

分析

自己的思路:类似贪心

设选择的客户数为x,所选客户数的总金额为xc

A% = x / n;B% = xc / tot

n,tot为定值,要B%-A%最大,就要xc尽可能大,x尽可能小

先把c[  ]从大到小排序,每次加入一个客户,如果差值变小就存下加之前的答案然后结束

——>因为c[ i ]是递减,若现在的c[ i ]加上后差值变小,则后面的c[ j ]无论如何也不会比当前c[ i ]更优,也就不会使差值变大了,因此可以直接结束

测完没想到这个思路只对了1/4的点,反思半天也没想出来,

最后发现居然是没开long long的锅(自己的思路没问题,开心)


正解思路:其实也是贪心...

和自己想的差不多,只是如果加入一个客户后,差值变大就更新答案,不管怎样仍然继续循环

AC代码-Myself

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=3e5;
ll c[MAXN+5];
ll n,tot,xc;
double tmp1,tmp2,Max=-10*1.0;
bool cmp(ll a,ll b)
{
	return a>b;
}
int main()
{
	//freopen("pareto.in","r",stdin);
	//freopen("pareto.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&c[i]);
		tot+=c[i];
	}
	sort(c+1,c+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		xc+=c[i];
		tmp1=(double)xc/tot;
		tmp2=(double)i/n;
		if(tmp1-tmp2>Max)
			Max=tmp1-tmp2;
		else
		{
			tmp1=(double)(xc-c[i])/tot;
			tmp2=(double)(i-1)/n;
			printf("%.3f\n%.10f\n",tmp2*100,tmp1*100);
			return 0;
		}
	}
	printf("%.3f\n%.10f\n",tmp2*100,tmp1*100);
	return 0;
}

AC代码-std版

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=3e5;
ll c[MAXN+5];
ll n,tot,xc;
double tmp1,tmp2,ans1,ans2,Max=-10*1.0;
bool cmp(ll a,ll b)
{
	return a>b;
}
int main()
{
	//freopen("pareto.in","r",stdin);
	//freopen("pareto.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&c[i]);
		tot+=c[i];
	}
	sort(c+1,c+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		xc+=c[i];
		tmp1=xc*100.0/tot;
		tmp2=i*100.0/n;
		if(tmp1-tmp2>Max)
		{
			Max=tmp1-tmp2;
			ans1=tmp1;
			ans2=tmp2;
		}
	}
	printf("%.3f\n%.3f\n",ans2,ans1);
	return 0;
}

三、Unija

题目

Sample Input 1

3
8 2
4 4
2 6

Sample Output 1

28

Sample Input 2

5
2 10
4 4
2 2
8 8
6 6

Sample Output 2

68

分析

考试时自己想到了把x或y排序,但是捣鼓半天没捣鼓对...果然能力还是不足...


改后思路:

因为矩形的中心在原点,图形为上下左右对称图形...所以可以把图形简化:只研究它的1/4

把各矩形按高度从大到小排序,从左至右扫一遍(扫描过程为红色的线“ fx ”)即可计算答案

若当前矩形为图中有深蓝色边框的,上一次处理后fx的位置大于(或等于)当前矩形的x则跳过

考试瞎搞导致WA-代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=(int)1e6;
int n,ans,add,max_y,tmp,flag;
struct node
{
	int x,y;
}a[MAXN+5];
bool cmp(node a,node b)
{
	if(a.x<=b.x&&a.y>=b.y)
		return true;
	if(a.x<=b.x)
		return true;
	else
		return false;
}
int main()
{
	//freopen("unija.in","r",stdin);
	//freopen("unija.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
		a[i].x/=2;
		a[i].y/=2;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		tmp=0,flag=0;
		//printf("x:%d y:%d\n",a[i].x,a[i].y);
		if(a[i].y>=a[i-1].y)
		{
			for(int j=1;j<=i-1;j++)
			{
				if(a[j].y>a[i].y)
				{
					tmp+=a[j].x*(a[j].y-a[i].y);
					flag=1;
				}
				else
					break;
			}
			add=a[i].x*a[i].y-ans+tmp;
		}
		//add=a[i].x*a[i].y-a[i-1].x*a[i-1].y;
		else
			add=(a[i].x-a[i-1].x)*a[i].y;
		ans+=add;
		//printf("tmp:%d\n",tmp);
		//printf("*%d %d\n",ans,add);
	}
	printf("%d\n",ans*4);
	return 0;
}

AC代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=(int)1e6;
ll n,ans,s,max_y=-1,max_x=-1,tmp,flag,fx;
struct node
{
	ll x,y;
}a[MAXN+5];
bool cmp(const node &a,const node &b)
{
	return a.y>b.y;
}
int main()
{
	//freopen("unija.in","r",stdin);
	//freopen("unija.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&a[i].x,&a[i].y);
		a[i].x/=2;
		a[i].y/=2;
		max_x=max(max_x,a[i].x);
		max_y=max(max_y,a[i].y);
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		if(a[i].x<=fx)
			continue;
		s+=1LL*a[i].y*(a[i].x-fx);
		fx=a[i].x;
	}
	printf("%I64d\n",s*4LL);
	return 0;
}

四、Ronald

题目

Sample Input 1

2

0

Sample Output 1

DA

Sample Input 2

3

2

1 2

2 3

Sample Output 2

NE

Sample Input 3

4

2

1 3

2 4

Sample Output 3

DA

分析

对于一条边“A—B”,对A或对B的操作次数都会改变该边的状态(有/没有),则该边的状态取决于A和B的操作次数是否相同

考虑到这一点,就假设每个点操作0次或1次(操作或不操作)即可,简化了任务

从顶点(1号点)开始,

(1)假设顶点先不操作,再确定其他点“K”是否操作,来保证存在边“1—K”,操作完后检查图中的点是否两两相连

(2)假设顶点操作,再重复(1)的方法

代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1000;
int a[MAXN+5][MAXN+5],b[MAXN+5][MAXN+5];
int n,m,u,v;
void Change(int a[MAXN+5][MAXN+5],int x)
{//去掉与x相连的边,加上不相连的边 
	for(int i=1;i<=n;i++)
	//可以直接写: a[x][i]=a[i][x]=1-a[i][x];
		if(a[x][i]==0)
			a[x][i]=a[i][x]=1;
		else
			a[x][i]=a[i][x]=0;
}
bool Check(int a[MAXN+5][MAXN+5]) 
{
	//将与顶点不相连的边变成相连 
	for(int i=2;i<=n;i++)
		if(!a[i][1]) 
			Change(a,i);
	//检查图中各点是否两两相连: 
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(a[i][j]==0)
				return false;
	return true;
}
int main()
{
	//freopen("ronald.in","r",stdin);
	//freopen("ronald.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		a[u][v]=a[v][u]=1;
		b[u][v]=b[v][u]=1;
	}
	if(Check(a))
	{
		printf("DA\n");
		return 0;
	}
	Change(b,1);//改变一次顶点的连接状态,再检查一次 
	if(Check(b))
	{
		printf("DA\n");
		return 0;
	}
	else
		printf("NE\n");
	return 0;
}

五、Poklon

题目

Sample Input 1

5 1

1 2 1 1 1

1 3

Sample Output 1

1

Sample Input 2

5 2

1 1 1 1 1

2 4

2 3

Sample Output 2

0

1

Sample Input 3

5 2

1 1 2 2 3

1 1

1 5

Sample Output 3

0

2

分析

首先恭喜自己考试中第三次MLE!

因为自己还没开始学写“莫队”算法(老师布置了题,但是我莫得时间),所以考试的时候不会打,只能瞎搞qwq...


接下来说正事...

看题目和数据范围可以确定方法:法一:莫队,法二:线段树

这里用的莫队模板

Ps.数据太大,要离散化(见函数Prepare( ))

MLE-考试瞎搞-代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=5e5,MAXT=1e8;
int a[MAXN+5],vis[MAXT+5],cnt[MAXT+5];
int n,q,l,r,ans;
int main()
{
	//freopen("poklon.in","r",stdin);
	//freopen("poklon.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	while(q--)
	{
		memset(vis,0,sizeof(vis));
		memset(cnt,0,sizeof(cnt));
		ans=0;
		scanf("%d%d",&l,&r);
		for(int i=l;i<=r;i++)
			cnt[a[i]]++;
		for(int i=l;i<=r;i++)
		{
			if(cnt[a[i]]==2&&!vis[a[i]])
			{
				vis[a[i]]=1;
				ans++;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

AC代码

特别鸣谢:CXH

#include<cstdio>
#include<map>
#include<vector>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=5e5,MAXT=1e8;
int a[MAXN+5],cnt[MAXN+5],ans[MAXN+5];
int n,m,res,K;
struct node
{
	int l,r,id;
}q[MAXN+5];
bool cmp(node a,node b)
{
	int x=(a.l+K-1)/K,y=(b.l+K-1)/K;
	if(x==y)
		return a.r<b.r;
	return x<y;
}
void Prepare()//将数组a离散化 
{
	map<int,bool> vis;
	map<int,int> rank;
	vector<int> g;
	for(int i=1;i<=n;i++)
		if(!vis[a[i]])
		{
			g.push_back(a[i]);
			vis[a[i]]=1;
		}
	sort(g.begin(),g.end());
	for(int i=0;i<g.size();i++)
		rank[g[i]]=i+1;
	for(int i=1;i<=n;i++)
		a[i]=rank[a[i]];
}
void Remove(int x)
{
	cnt[a[x]]--;
	if(cnt[a[x]]==1)
		res--;
	if(cnt[a[x]]==2)
		res++;
 } 
void Add(int x)
{
	cnt[a[x]]++;
	if(cnt[a[x]]==3)
		res--;
	if(cnt[a[x]]==2)
		res++;
}
int main()
{
	//freopen("poklon.in","r",stdin);
	//freopen("poklon.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	K=sqrt(n);
	Prepare();
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	int dl=0,dr=0;
	for(int i=1;i<=m;i++)
	{
		int L=q[i].l,R=q[i].r;
		while(dl<L)
			Remove(dl++);
		while(dr>R)
			Remove(dr--);
		while(dl>L)
			Add(--dl);
		while(dr<R)
			Add(++dr);
		ans[q[i].id]=res;	
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值