AtCoder Regular Contest 098 题解

40 篇文章 0 订阅
38 篇文章 0 订阅

C - Attention


sb题,我们前缀后缀和一下直接O(N)算贡献就可以了

#include <stdio.h>
#include <string.h>
#include <algorithm>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int INF=0x3f3f3f3f;
const int N=500005;

char s[N];

int main(void) {
	int n; scanf("%d%s",&n,s+1);
	int L=0,R=0,ans=INF;
	rep(i,1,n) R+=(s[i]=='E');
	rep(i,1,n) {
		R-=(s[i]=='E');
		ans=std:: min(ans,L+R);
		R+=(s[i]=='W');
	}
	printf("%d\n", ans);
	return 0;
}

D - Xor Sum 2


有一个小结论就是a^b<=a+b恒成立。证明的话,可以发现异或运算被称为不进位加法?
因此做法就十分显然了。我们枚举右端点,那么合法的左端点一定是连续一段,这个可以用二分找出来。
预处理前缀和前缀异或和就是O(NlogN)了

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
#define copy(x,t) memcpy(x,t,sizeof(x))

#define fi first
#define se second

typedef std:: pair <int,int> pair;
typedef long double ld;
typedef long long LL;

const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
const int ny2=(MOD+1)/2;
const int N=2000005;
const ld pi=acos(-1);
const ld e=exp(1);
const ld eps=1e-8;

LL a[N],s[N],t[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	int n=read();
	rep(i,1,n) {
		a[i]=read();
		s[i]=s[i-1]+a[i];
		t[i]=t[i-1]^a[i];
	}
	LL ans=0;
	rep(i,1,n) {
		int l=1,r=i,pos=1;
		while (l<=r) {
			int mid=(l+r)>>1;
			if ((s[i]-s[mid-1])==(t[i]^t[mid-1])) {
				r=mid-1,pos=mid;
			} else l=mid+1;
		}
		ans+=(i-pos+1);
	}
	printf("%lld\n", ans);
	return 0;
}

E - Range Minimum Queries


考虑枚举最小值mn。我们钦定比mn小的数都不能取
为了使答案最小,我们需要选取能选的数中第k小的,记为mx,然后用mx-mn更新答案就可以了
找哪些数字可选就直接用vector抠出来,然后std排序就完了。这样做是O(N2logN) 的

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int INF=0x3f3f3f3f;
const int N=20005;

int a[N],b[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	freopen("data.in","r",stdin);
	int n=read(),k=read(),q=read();
	rep(i,1,n) b[i]=a[i]=read();
	std:: sort(b+1,b+n+1);
	int ans=INF;
	rep(ti,1,n) {
		int mn=b[ti];
		std:: vector <int> v,r;
		v.push_back(0);
		rep(i,1,n) if (a[i]<mn) v.push_back(i);
		v.push_back(n+1);
		int pre=v[0];
		for (int i=1;i<v.size();++i) {
			int now=v[i];
			int len=now-pre-1;
			if (len<k) {pre=now; continue;}
			std::vector <int> s;
			rep(j,pre+1,now-1) s.push_back(a[j]);
			std:: sort(s.begin(),s.end());
			rep(j,0,len-k) r.push_back(s[j]);
			pre=now;
		}
		std:: sort(r.begin(),r.end());
		if (q<=r.size()) ans=std:: min(ans,r[q-1]-mn);
	}
	printf("%d\n", ans);
	return 0;
}

F - Donation


连续两次都读错题意了。。语死早吃枣药丸
感觉这题都讲得好感性啊。。要不意识流一波混过去吧。。

记C[i]=max(A[i]-B[i],0),那么A的限制就变成了,除了要捐的B元,我们还需要多带上C这么多钱

很容易发现我们肯定优先捐C较小的,也就是意味着我们可以多次经过某个位置但是只有最后一次捐钱

并且可以知道,我们一定是选择一个C最大的位置x作为起点,出发捐完若干连通块->回到x并捐x->捐剩下的一个连通块(当然也可能没有剩下)

为啥呢?首先我们发现随着访问节点变多,答案是不降的。那么就可能存在某个位置,使得它在x前捐不会让答案变小,在x后捐不会让答案变大。因此是可能存在连通块剩余的。
其次可以知道捐完x之后若有连通块剩余,那么一定只会剩下一个连通块。因为我们不会再回到x

观察到每个连通块与原图的做法是一模一样的,于是我们按照C排序,枚举每一个连通块的根然后建出树,在树上dp就可以了

			
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const LL INF=1e18;
const int N=200005;

std:: vector <int> E[N],G[N];

LL a[N],b[N],f[N],g[N];
int fa[N],rk[N];

bool vis[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void dfs(int x) {
	if (G[x].empty()) {
		f[x]=a[x]+b[x],g[x]=b[x];
		return ;
	}
	g[x]=b[x],f[x]=INF;
	for (int y:G[x]) dfs(y),g[x]+=g[y];
	for (int y:G[x]) {
		f[x]=std:: min(f[x],std:: max(f[y],a[x])+g[x]-g[y]);
	}
}

int find(int x) {
	return !fa[x]?x:(fa[x]=find(fa[x]));
}

bool cmp(int x,int y) {
	return a[x]<a[y];
}

int main(void) {
	freopen("data.in","r",stdin);
	int n=read(),m=read();
	rep(i,1,n) {
		a[i]=read(),b[i]=read();
		a[i]=std:: max(a[i]-b[i],0LL);
		rk[i]=i;
	}
	rep(i,1,m) {
		int x=read(),y=read();
		E[x].push_back(y);
		E[y].push_back(x);
	}
	std:: sort(rk+1,rk+n+1,cmp);
	rep(ti,1,n) {
		int x=rk[ti]; vis[x]=1;
		for (int y:E[x]) {
			if (!vis[y]) continue;
			if (find(y)==find(x)) continue;
			G[x].push_back(find(y));
			fa[find(y)]=find(x);
		}
	}
	rep(i,1,n) if (find(i)==i) {
		dfs(i),printf("%lld\n", f[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值