5月AT杂题

感觉越写越像一句话题解了。

AtCoder Beginner Contest 303

E - A Gift From the Stars

对于一个度数大于 \(2\) 的点,它显然是一个菊花的中心,周围相邻的就是附加边。把这些菊花删除以后剩下的一定都是三个点的两级菊花图,直接根据剩余点的数量输出即可。

F - Damage over Time

下文用语:总伤(\(t_i\times d_i\))、dot(\(d_i\))、输出周期(\(t_i\))。

先把没用的删掉(按 \(t_i\) 从小到大排序,\(t_i\) 相同的只保留 \(d_i\) 最大的),再二分答案。可以把这个时间根据分成一段一段的:对于剩余时间在 \([t_{i-1}+1,t_i]\) 这段,它前边的 \((t_j,d_j),j<i\) 每一次发动都可以贡献一个完整的总伤,它后边的 \((t_k,d_k),k\ge i\) 总伤打不满,只能确定 dot 为 \(d_k\)。显然可以找到一个分界点满足前边能输出的时间短,尽量用短输出周期的魔法打满总伤;后边能输出的时间长,可以用高 dot 的魔法力大砖飞(可能只会选择其中一种方式)。

注意中间值太大,会溢出。

code:

点击查看代码
#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
int add_check(int x,int y){
	if(x<y)swap(x,y);
	if(inf-x>=y)return x+y;
	else return inf;	
}
int mul_check(int x,int y){
	if(x<y)swap(x,y);
	if(inf/x>=y)return x*y;
	else return inf;
}
int ask(int l,int r){
	if(l>r)return 0;
	if((r-l+1)%2==0)return mul_check((r-l+1)/2,l+r);
	else return mul_check(r-l+1,(l+r)/2);
}
struct Node{
	int t,d;
}a[300005],b[300005];
int cmp(Node x,Node y){
	if(x.t!=y.t)return x.t<y.t;
	return x.d>y.d;
}
int n,m,h,TD[300005],D[300005];
int check(int mid){
	int sum=0;
	for(int i=1;i<=n+1;i++){
		int l=a[i-1].t+1,r=min(a[i].t,mid);
		if(l>r)continue;
		if(i==1)sum=add_check(sum,mul_check(ask(l,r),D[i]));
		else if(i==n+1)sum=add_check(sum,mul_check(r-l+1,TD[i-1]));
		else{
			int pos=TD[i-1]/D[i];
			if(l<=pos)sum=add_check(sum,mul_check(min(pos,r)-l+1,TD[i-1]));
			if(pos<r)sum=add_check(sum,mul_check(ask(max(pos+1,l),r),D[i]));
		}
	}
	return (sum>=h);
}
signed main(){
	n=read(),h=read();
	for(int i=1,t,d;i<=n;i++){
		t=read(),d=read(),a[i]=(Node){t,d};
	}
	sort(a+1,a+n+1,cmp);b[0].t=-1;
	for(int i=1;i<=n;i++){
		if(b[m].t!=a[i].t)b[++m]=a[i];
	}
	n=m,a[m+1].t=inf;for(int i=1;i<=m;i++)a[i]=b[i];
	TD[0]=-inf;for(int i=1;i<=n;i++)TD[i]=max(TD[i-1],a[i].t*a[i].d);
	D[n+1]=-inf;for(int i=n;i>=1;i--)D[i]=max(D[i+1],a[i].d);
	int l=0,r=h,res=h;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))res=mid,r=mid-1;
		else l=mid+1;
	}
	print(res);
	return 0;
}

AtCoder Regular Contest 161

比较简单。

A - Make M

蠢。先排序,把奇数位置赋成最小的那些、偶数位置赋成剩下的即可。

B - Exactly Three Bits

更蠢。枚举前两位找第三位即可。

C - Dyed by Majority (Odd Tree)

我很蠢。小错误写挂 n 遍。

定义 \(f_{i,0/1}\) 表示 \(i\)\(fa_i\) 原始颜色为 \(0/1\) 时能选哪些原始颜色。dp 一遍求是否可行,再 dp 一遍求方案。

D - Everywhere is Sparser than Whole (Construction)

先判连不够 \(dn\) 条边的情况。手玩几组小样例看可以看出一种方案:每个点向它后面 \(d\) 个点连边(循环)。交上去,你会发现它过了。

为什么能过?假设每次都是最坏的情况,你每次删的点会带走 \(2d,2d-1\ldots1\) 条边,你发现越后面越可能寄,但是算一下发现最后平均下来也就密度为 \(d\)

posted @ 2023-05-02 19:49  xx019  阅读(21)  评论(0编辑  收藏  举报