8.8

image

ABC417_D,luogu P9401 ,ABC409_F

ABC417_D

赛时想的是记录当开始时初始值在不同区间时所对的答案

对于每一次收到礼物,会产生三个区间

1.p[i]>=now 对于区间0p[i]增加一个a[i]
2.p[i]<now&&now>=b[i] 对于区间max(p[i]+1,b[i])inf减去一个b[i]
3.p[i]<now&&now<b[i] 对于区间p[i]+1b[i]-1设为0

但是这么做会让程序十分复杂赛时没有调出来,而且对于初始值极大的问题就束手无策了

考虑动态规划,注意数据范围,所有涉及心情变化的值均小于等于500

那么可以说明,当一个1000以内的心情加入运算时,这个值一定会持续保留在1000以内

设计dp[i][j]表示第i次运算前,当前心情为j,最后会达到的心情

对于初始值极大的问题,保留dp的第一维(也就是第i次运算时),二分需要减去多少才会达到1000以内

然后在dp的当前次运算中取答案即可,时间复杂度为O(mn + q log n)

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e4+5;
const int maxm=1005;
const int m=1000;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int p[maxn],a[maxn],b[maxn];
int dp[maxn][m];
int sum[maxn];
int n,q;
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(p[i]),read(a[i]),read(b[i]);
		sum[i]=sum[i-1]+b[i];
	}
	for(int i=0;i<=m;i++) dp[n+1][i]=i;
	for(int i=n;i>=1;i--){
		for(int j=0;j<=m;j++){
			if(p[i]>=j) dp[i][j]=dp[i+1][j+a[i]];
			else dp[i][j]=dp[i+1][max(j-b[i],0)];
		}
	}
	read(q);
	int in;
	while(q--){
		read(in);
		int i=lower_bound(sum,sum+n+1,in-m)-sum;
		//cout<<i<<" ans=";
		if(i==n+1) printf("%d\n",in-sum[n]);
		else printf("%d\n",dp[i+1][in-sum[i]]);
	}
	return 0;
}
//^o^

luogu P9401

先顺道去学了个binary_gcd,这篇题解挺好的

附上我的代码(luogu P5435

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=5005;
const int mod=998244353;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int a[maxn],b[maxn];
int n;
int gcd(int x,int y){
	int xi=__builtin_ctz(x),yi=__builtin_ctz(y);
	int z=min(xi,yi);
	y>>=yi;
	int temp;
	while(y){
		x>>=xi;
		temp=y-x;
		xi=__builtin_ctz(temp);
		y=(y>x ? x : y);
		x=(temp<0 ? -temp : temp);
	}
	return x<<z;
}
int main(){
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++) read(b[i]);
	LL ans,p;
	for(int i=1;i<=n;i++){
		ans=0,p=i;
		for(int j=1;j<=n;j++){
			ans+=p*gcd(a[i],b[j]);
			ans%=mod;
			p=p*i%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
//^o^

因为a的数据范围较小,把b按照a放到

特判全选b的情况,否则如果选了至少一个a答案一定在5e5以内,直接枚举答案即可

对答案的验证:如果ans|a[i]选择a[i],否则选b[i]

既然已经建桶了,用st维护一下区间gcd即可

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
const int maxlog=20;
const int maxm=5e5;
void read(LL& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
LL gcd(LL x,LL y){
	if((!x)|(!y)) return x|y;
	int xi=__builtin_ctzll(x),yi=__builtin_ctzll(y);
	int z=min(xi,yi);
	y>>=yi;
	LL temp;
	while(y){
		x>>=xi;
	    temp=y-x;
		xi=__builtin_ctzll(temp);
		y=(y>x ? x : y);
		x=(temp<0 ? -temp : temp);
	}
	return x<<z;
}
LL st[21][maxm+5];
LL n;
void init_st(){
	for(int i=1;i<=maxlog;i++){
		for(int l=1,r=(1<<i);r<=maxm;l++,r++){
			st[i][l]=gcd(st[i-1][l],st[i-1][l+(1<<(i-1))]);
		}
	}
}
LL query(int l,int r){
	int i=log2(r-l+1);
	return gcd(st[i][l],st[i][r-(1<<i)+1]);
}
int main(){
	read(n);
	LL a,b;
	for(int i=1;i<=n;i++){
		read(a),read(b);
		st[0][a]=gcd(st[0][a],b);
	}
	init_st();
	LL t=query(1,maxm);
	for(int i=maxm;i>t;i--){
		LL ans=0;
		for(int j=i;j<=maxm;j+=i){
			ans=gcd(ans,query(j-i+1,j-1));
			if(ans%i) break;
		}
		if(maxm%i) ans=gcd(ans,query(maxm/i*i+1,maxm));
		if(ans%i==0){
			printf("%d",i);
			return 0;
		}
	}
	printf("%lld",t);
	return 0;
}
//^o^

ABC409_F

我为什么会留一道水题

评定为中模拟一道

呃,建个优先队列保存点的距离,再建个dsu保存连通块就好

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
#define abs(x) ((x)<0 ? -(x) : (x))
using namespace std;
typedef long long LL;
const int maxn=3005;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
struct edge{
	int a,b;
	LL dis;
	edge(){ }
	edge(int ai,int bi);
	bool operator<(edge a)const;
};
int x[maxn],y[maxn];
int cnt=0;
edge::edge(int ai,int bi){
	a=ai,b=bi,dis=abs(x[ai]-x[bi])+abs(y[ai]-y[bi]);
}
bool edge::operator<(edge a)const{
	return dis>a.dis;
}
int f[maxn];
int find_f(int u){
	if(f[u]==u) return u;
	else return f[u]=find_f(f[u]);
}
void merge_f(int u,int v){
	int x=find_f(u),y=find_f(v);
	f[x]=y;
}
priority_queue<edge> q;
void add_dot(int xi,int yi){
	++cnt;
	x[cnt]=xi,y[cnt]=yi;
	f[cnt]=cnt;
	for(int i=1;i<cnt;i++){
		q.push(edge(i,cnt));
	}
}
LL merge_dot(){
	while((!q.empty())&&(find_f(q.top().a)==find_f(q.top().b))){
		q.pop();
	}
	if(q.empty()) return -1;
	LL d=q.top().dis;
	while((!q.empty())&&q.top().dis==d){
		edge e=q.top();
		q.pop();
		if(find_f(e.a)==find_f(e.b)) continue;
		merge_f(e.a,e.b);
	}
	return d;
}
int n,t;
int main(){
	read(n),read(t);
	int xi,yi;
	for(int i=1;i<=n;i++){
		read(xi),read(yi);
		add_dot(xi,yi);
	}
	while(t--){
		int op;
		read(op);
		if(op==1){
			read(xi),read(yi);
			add_dot(xi,yi);
		}
		else if(op==2){
			printf("%lld\n",merge_dot());
		}
		else{
			read(xi),read(yi);
			if(find_f(xi)==find_f(yi)){
				printf("Yes\n");
			}
			else{
				printf("No\n");
			}
		}
	}
	return 0;
}
//^o^
posted @ 2025-08-08 19:21  huangems  阅读(12)  评论(0)    收藏  举报