【题解】Codeforces Round #509 (Div. 2) ABCDEF

版权声明:欢迎转载评论 https://blog.csdn.net/m0_37809890/article/details/82744086

Speedforces,C题速度卡了,F题没想到,然后就凉了。

总结

  1. 如果需要对多属性的元素按一个属性进行排序,删除,lower_bound等操作时,可以选择建立结构体扔到set里,也可以选择用map:键就是排序的属性,值可以是结构体,也可以是下标,这样实现的难度会远小于set。
  2. 又一次双指针,for套while,也许之后会系统的总结一下。
  3. 思维:考虑哪些状态一定比哪些状态优(控制关系),从而减少需要排查的状态个数。

1041A. Heist

排序,输出最后一项减第一项-n+1

1041B. Buying a TV Set

给x,y约分,输出min(a/x,b/y)

1041C. Coffee Break 贪心

给定长为n的整数序列,表示需要在这些时刻进行喝茶,一天最多有m分钟。每次喝茶持续一分钟,给定两次喝茶间的最小间隔d,问最少几天可以皮皮完,按输入顺序输出每次喝茶所在天数。

显然贪心,每次选择大于已选值d以上的第一个,当天放不下就新开一天。如果输出答案只需输出最少天数,使用set维护这些时刻即可。但是需要记录答案,所以用map来写。

int ans[M];
int main(void)
{
	int n = read(),m=read(),d=read();
	map<int,int> mp;
	for(int i=1;i<=n;i++)
		mp[read()] = i;

	int day = 0;
	while(!mp.empty())
	{
		day++;
		int st = 0;
		map<int,int>::iterator it;
		while((it = mp.lower_bound(st))!=mp.end())
		{
			ans[it->second] = day;
			st = it->first+d+1;
			mp.erase(it);
		}
	}
	printf("%d\n",day );
	for(int i=1;i<=n;i++)
		printf("%d ",ans[i] );

    return 0;
}

1041D. Glider 双指针:滑动窗口

首先肯定在风口的最左边开始,而且飞行的路程 = 高度 + 可以走的风口距离。如果从左到右枚举起始的风口,可以走的最后一个风口编号也是单调不减的,每次检查一下即可。

ll wind[M],sumwind[M];
ll fall[M];
int main(void)
{
	ll n = read(), h = read();

	ll x1 = read(), x2 = read();
	for(int i=1;i<n;i++)
	{
		wind[i] = x2 - x1;
		ll p1 = read(), p2 = read();
		fall[i] = p1 - x2;
		x1 = p1, x2 = p2;
	}
	wind[n] = x2 - x1;
	for(int i=1;i<=n;i++)
		sumwind[i] = sumwind[i-1] + wind[i];

	ll ans = 0, cost = 0, up = 0;
	for(int i=1;i<=n;i++)
	{
		cost -= fall[i-1];
		while(up<n&&cost<h)
		{
			cost += fall[++up];
		}
		ans = std::max(ans,sumwind[up]-sumwind[i-1]);
	}

	printf("%I64d\n",ans+h );
    return 0;
}

1041E. Tree Reconstruction 构造,树

首先所有的对必须一个是n,另一个不是n,然后从1到n-1计算一下它们个数的前缀和,如果sum[j]>j则也说明不行。
原理是从1到n-1所有的数都本该出现一次,如果没有出现,就说明它至少有两边连着比他都大的数。
构造一条链,从1到n-1遍历个数,如果为0就放在队列里,如果为1就放在链右端,大于1就先放在链右端,再在右端添加r-1个队列里的数。最后将n放在最右端。

int save[M];
int ans[M];
int main(void)
{
	int n = read(), fail = 0;
	for(int i=1;i<n;i++)
	{
		int a = read(), b = read();
		if(a==n&&b==n) fail = 1;
		if(a!=n&&b!=n) fail = 1;
		if(a==n) save[b]++;
		if(b==n) save[a]++;
	}
	for(int i=1,sum=0;i<n;i++)
	{
		sum += save[i];
		if(sum>i) fail = 1;
	}
	if(fail) return !printf("NO\n");
	queue<int> die;
	int j = 1;
	for(int i=1;i<n;i++)
	{
		if(save[i]==0) 
			die.push(i);
		else
		{
			ans[j++] = i;
			for(int k=1;k<save[i];k++)
			{
				ans[j++] = die.front();
				die.pop();
			}
		}
	}
	ans[n] = n;
	printf("YES\n");
	for(int i=1;i<n;i++)
	{
		printf("%d %d\n",ans[i],ans[i+1] );
	}
    return 0;
}

1041F. Ray in the tube 思维

问题1:上下两条线(1e9)上分布着若干传感器(1e5),选择下方线上的一个整点A,上方线上的一个整点B,从A到B发射一道可以反射的光,问光最多经过几个传感器(A,B上的也算)

想不出来

问题2:有一条线上有若干个传感器,选择线上一个整点A,一个间距C(C>=2且为偶数),发射一个跳弹,跳弹会经过A,A+C,A+2C,…,问最多经过几个传感器。

想不出来

问题3:有一条线上由若干个传感器,给定间距C,选择线上的一个整点A发射跳弹,跳弹会经过A,A+C,A+2C,…,问最多经过几个传感器。

给所有传感器的坐标取模,答案就是最多的模值分布。

一个结论是,间距C的情况会比间距xC的情况更优(x>1)。
在两条线的时候,画一下会发现,间距C的情况会比间距xC的情况更优(x为奇数),所以只需要枚举C=1,2,4,8,16,…即可。每次枚举时把模数分布放在map里。
总复杂度 O ( n l o g ( 1 e 9 ) l o g n ) O(nlog(1e9)logn)

int A[M],B[M];
int main(void)
{
	int n = read(); read(); for(int i=0;i<n;i++) A[i] = read();
	int m = read(); read(); for(int i=0;i<m;i++) B[i] = read();

	int ans = 2;
	for(int step = 2;step<=MOD;step<<=1)
	{
		int tmp = 0;
		std::map<int,int> mp;
		for(int i=0;i<n;i++)
			tmp = std::max(tmp,++mp[A[i]%step]);
		for(int i=0;i<m;i++)
			tmp = std::max(tmp,++mp[(B[i]+(step>>1))%step]);
		ans = std::max(ans,tmp);
	}
	printf("%d\n",ans );

    return 0;
}

附:这个问题2我没想到做法。

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/82744086