4月7日 测试题

第二天的测试。觉得还是有点进步。再接再厉。

代码风格开始转变,用了很多简写。主要是懒


简单模拟,不想多说什么。

一开始想到用Map[maxn][maxn]来打标记的做法,但是n范围达1e5所以会炸,然后就想到用变量表示行列,每次遇到坐标就减1就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e5+5;

int n,m;
int x,y;
int h[MAXN],l[MAXN];
int sumh,suml;
long long ans;

int main(){
//	freopen("chess.in","r",stdin);
//	freopen("chess.out","w",stdout);
	cin>>n>>m;
	ans=(long long)n*n;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		if(!h[x]){
			h[x]=1;
			ans-=n-suml;
			sumh++;
		}
		if(!l[y]){
			l[y]=1;
			ans-=n-sumh;
			suml++;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

自己开始以为是一道简单题就简单的模拟了过程,没想到数据量这么大。。。

开始的模拟代码(错的):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<cctype>
#define ll long long
#define in(x) scanf("%d",&x)
#define lin(x) scanf("%lld",&x)
#define sin(x) scanf("%s",x)
#define chin(x) scanf("%c",&x)
#define out(x) printf("%d",x)
#define lout(x) printf("%lld",x)
using namespace std;
const int MAXN = 1e5 + 5;
ll n,m;
struct node
{
	ll fa;
	ll v;
}Map[MAXN];

int main()
{
//	freopen("graph.in","r",stdin);
//	freopen("graph.out","w",stdout);
	lin(n);lin(m);
	
	for(int i = 0;i < n;i++)
	{
		lin(Map[i].fa);
	}
	
	for(int i = 0;i < n;i++)
		lin(Map[i].v);
		
	for(int i = 0;i < n;i++)
	{	
		ll sum = 0;
		ll minv = 1e9;
		ll temp = m;
		int t1 = i;
		while(temp--)
		{
			
			if(Map[t1].fa == t1)
			{
				sum += Map[t1].v;
				minv = min(minv,Map[t1].v);
				continue;
			}
			
			minv = min(minv,Map[t1].v);
			sum += Map[t1].v;
			t1 = Map[t1].fa; 
		}		
		lout(sum);
		putchar(' ');
		lout(minv);
		putchar('\n');
	}
	return 0;
}
下次得注意效率的重要性!!!!

正解用到了倍增的思想,相当于把原本n*k(k可达1e10!)的效率加快到n*log2(k)的速度。

类似于LCA的过程,但是要简单一些。

Map[i][j]代表i节点的第2^j次方祖先(j == 0 就是本身啦)。

做的过程迷之wrong了很久,后来发现是最后求sum和minx的那个循环中1<<l超出了int范围,导致加和错误。。。。(整心隐蔽啊这!!)

#include<bits/stdc++.h>
#define ll long long
#define in(x) scanf("%d",&x)
#define lin(x) scanf("%lld",&x)
#define sin(x) scanf("%s",x)
#define chin(x) scanf("%c",&x)
#define out(x) printf("%d",x)
#define lout(x) printf("%lld",x)
using namespace std;
const int MAXN = 1e5 + 5;
const int LMT = 40;
ll k;
int n;
ll p[LMT];
struct node
{
	int fa,minv;
	ll sum;
}Map[MAXN][LMT];

void Init()
{
	for(int l = 1;l <= log2(k);l++)
	{
		for(int i = 0;i < n;i++)
		{
			Map[i][l].fa = Map[Map[i][l - 1].fa][l - 1].fa;
			Map[i][l].sum = Map[i][l - 1].sum 
			+ Map[Map[i][l - 1].fa][l - 1].sum;
			Map[i][l].minv = 
			min(Map[i][l - 1].minv,Map[Map[i][l - 1].fa][l - 1].minv);	
		}
	}
	return;
}
int main()
{
	in(n);lin(k);
 	for(int i=p[0]=1;i<=log2(k);i++) p[i]=p[i-1]*2;
	for(int i = 0;i < n;i++)
	{
		in(Map[i][0].fa);
	}
	for(int i = 0;i < n;i++)
	{
		in(Map[i][0].minv);
		Map[i][0].sum = Map[i][0].minv;
	}
	Init();
	for(int i = 0;i < n;i++)
	{
		ll sum = 0,lgt = k;
		int minx = INT_MAX,idx = i;
		for(int j = log2(k);j >= 0;j--)
		{
			if(lgt >= p[j])
			{
				lgt -= p[j];
				
				if(Map[idx][j].minv < minx)
					minx = Map[idx][j].minv;
				
				sum += Map[idx][j].sum;
				
				idx = Map[idx][j].fa;
				
			}
		}
		lout(sum);
		putchar(' ');
		out(minx);
		putchar('\n');
	}
	return 0;
}

#1113. 【NOIP2018 模拟赛day2】能量获取

 统计

能量获取

Description “封印大典启动,请出Nescafe魂珠!”随着圣主applepi一声令下,圣剑护法rainbow和魔杖护法freda将Nescafe魂珠放置于封印台上。封印台是一个树形的结构,魂珠放置的位置就是根节点(编号为0)。还有n个其他节点(编号1-n)上放置着封印石,编号为i的封印石需要从魂珠上获取Ei的能量。能量只能沿着树边从魂珠传向封印石,每条边有一个能够传递的能量上限Wi,魂珠的能量是无穷大的。作为封印开始前的准备工作,请你求出最多能满足多少颗封印台的能量需求?

注意:能量可以经过一个节点,不满足它的需求而传向下一个节点。每条边仅能传递一次能量。

Input

第一行一个整数n,表示除根节点之外的其他节点的数量。

接下来n行,第i+1行有三个整数Fi、Ei、Wi,分别表示i号节点的父节点、i号节点上封印石的能量需求、连接节点i与Fi的边最多能传递多少能量。

Output

最多能满足多少颗封印石的能量需求。

Sample Input

4

0 3 2

0 100 100

1 1 1

2 75 80

Sample Output

2

Data Constraint

对于100%的数据,满足1<=n<=1000,0<=Fi<=n,0<=Ei,Wi<=100

----------------------------------------------------------------------------------------------------------------------------------最后一道题在考试时困扰了我最久,结果爆零

好吧我一开始思路是看到树,好!从根节点开始向后贪心试试!然后打死写不出来。

然后正解是从所需能量最小的节点开始向前贪心。。。quq

好吧是我的错我居然看到指向父节点不从后往前而是从前往后推,可能在折磨自己

ok,这道题其实就先以所需能量大小为关键字进行从小到大的排序,然后再依次判断1、这个点所需的能量上边每一条边还剩下的可传递能量够不够满足此点,不够就判断下一个点2、若都足够满足此点但是此点无法到达0点(就那啥魂珠)也不能构成通路,也舍弃此点判断下一点。(这里就相当于代码47到55行的内容,可以感性的理解一下)

若啥都满足的话就将其加入我们答案中,用掉一部分的上面每一条边的可传递能量,因为我们之前对每一个点的所需能量从小到大进行了排序,所以这里我们可以保证将传递能量最优地利用了,可以证明是正确的。

对n个点进行枚举判定,最后得到答案。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<cctype>
#include<queue>
#define in(x) scanf("%d",&x)
#define lin(x) scanf("%lld",&x)
#define sin(x) scanf("%s",x)
#define chin(x) scanf("%c",&x)
#define out(x) printf("%d",x)
#define lout(x) printf("%lld",x)
using namespace std;
const int MAXN = 1e3 + 5;
int n;
int ans = 0;

struct node
{
	int idx;
	int need;
	
	bool operator < (const node &x) const
	{
		return need < x.need;
	}
	
}Map[MAXN];

int fa[MAXN],wet[MAXN];

int main()
{
	in(n);
	for(int i = 1;i <= n;i++)
	{
		Map[i].idx = i;
		in(fa[i]);
		in(Map[i].need);
		in(wet[i]);
	}
	sort(Map + 1,Map + n + 1);
	for(int i = 1;i <= n;i++)
	{
		int j = Map[i].idx;
		while(j)
		{
			if(Map[i].need > wet[j]) break;
			j = fa[j];
		}
		if(j) continue;
		
		j = Map[i].idx;
		
		while(j)
		{
			wet[j] -= Map[i].need;
			j = fa[j];
		}
		ans++;
	}
	out(ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/79844996