2022.9.28

一测:190pts

估计:

T1:(黄,1200)

T2:(蓝,2000)

T3:(紫,2600)

T4:(黑,3400)


牛牛的方程式
【题目描述】
牛牛最近对三元一次方程非常感兴趣。众所周知,三元一次方程至少需要三个方
程组成一个方程组,才有可能得出一组解。
牛牛现在想要知道对于方程ax + by + cz = d中有没有至少存在一组{x, y, z}的解,且x, y, z都为整数,使得方程式成立。
【输入格式】
第一行输入一个正整数 T,表示测试点中测试样例的组数。
接下来T行,每行四个整数a, b, c, d表示方程ax + by + cz = d中的a, b, c, d
【输 格式】
如果至少存在一组x, y, z能够满足方程式等式成立,且x, y, z均为整数,请输出
"YES",否则请输出"NO"。
【样例 1 输入】
2
3 1 2 0
2 8 8 3
【样例 1 输出】
YES
NO
【样例 1 解释】
1*3+(-1)*1+(-1)*2=0
得到一组 x,y,z 的解为{1,-1,-1}为整数使得等式成立,所以输出"YES"。
不存在 x,y,z 为整数使得方程 2x+8y+8z=3 成立,所以输出"NO"。
【数据范围】
对于10%的测试数据,保证T = 1,−10 ≤ a, b, c ≤ 10
对于30%的测试数据,保证−100 ≤ a, b, c ≤ 100
对于100%的测试数据,保证−10^18 ≤ a, b, c ≤ 10^18,1 ≤ T ≤ 100

裴蜀定理。

不用也可以,判断 k0(modgcd(a,b,c)) 即可。

特判下 gcd(a,b,c)=0 即可。

#include<iostream>
#include<cstdio>
#define int long long
int gcd(int x,int y)
{
	if(x==0)return y;
	if(y==0)return x;
	return gcd(y,x%y);
}
signed main()
{
	freopen("niuniufx.in","r",stdin);
	freopen("niuniufx.out","w",stdout);
	int T;
	scanf("%lld",&T);
	while(T--)
	{
		int a,b,c,d;
		scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
		if(d==0)printf("YES");
		else if((gcd(a,b)!=0&&d%gcd(a,b)==0)||(gcd(a,c)!=0&&d%gcd(a,c)==0)||(gcd(c,b)!=0&&d%gcd(b,c)==0))printf("YES");
		else printf("NO");
		putchar('\n');
	}
	return 0;
}

牛牛的猜球游戏
【题目描述】
牛牛和牛妹在玩猜球游戏,牛牛首先准备了 10 个小球,小球的编号从 0~9。首先
牛牛把这 10 个球按照从左到右编号为 0,1,2,3...9 的顺序摆在了桌子上,接下来牛
牛把这 10 个球用 10 个不透明的杯子倒扣住。
牛牛接下来会按照一定的操作顺序以极快的速度交换这些杯子。
换完以后他问牛妹你看清楚从左到右的杯子中小球的编号了么?
由于牛妹的动态视力不是很好,所以她跑来向你求助。你在调查后发现牛牛置换
杯子其实是有一定原则的。
具体来讲,牛牛有一个长度大小为 n 的操作序列。
操作序列的每一行表示一次操作都有两个非负整数 a,b,表示本次操作将会交换从
左往右数第 a 个杯子和从左往右数第 b 个杯子(ab 均从 0 开始数)。请注意
是换杯子,而不是直接交换 a 号球和 b 号球。
牛牛和牛妹一共玩了 m 次猜球游戏,在每一轮游戏开始时,他都将杯子中的小球
重置到从左往右依次为 0,1,2,3...9 的状态。
然后在第 i 轮游戏中牛牛会按照操作序列中的第li个操作开始做,一直做到第ri个
操作结束(l和r 的编号从 1 开始计算)。
由于你提前搞到了牛牛的操作序列以及每一次游戏的 l,r。请你帮助牛妹回答出牛牛
每一轮游戏结束时,从左至右的杯子中小球的编号各是多少。
【输入格式】
首先输入一行两个正整数 n,m,表示操作序列的长度以及进行游戏的次数。
接下来 n 行每行两个非负整数 a,b,表示交换左数第 a 个杯子和左数第 b 个杯子。(a,b 均从 0 开始数起)
接下来 m 行每行两个正整数 l,r 表示该轮游戏中牛牛从第 l 个操作开始做,一直
做到第 r 个操作结束。(l 和r 的编号从 1 开始计算)
【输出格式】
对于每一轮游戏,输出一行 10 个非负整数,表示从左至右每一个杯子中小球,
输出的整数之间用空格隔开,行末不允许有多余空格。
【样例 1 输入】
5 3
0 1
1 2
2 3
0 1
9 0
1 5
5 5
3 5
【样例 1 输 】
9 1 3 0 4 5 6 7 8 2
9 1 2 3 4 5 6 7 8 0
9 0 3 2 4 5 6 7 8 1
【数据范围】
对于30%的测试数据,保证1 ≤ n, m ≤ 5006 页 共 11 页
对于60%的测试数据,保证1 ≤ n, m ≤ 4 × 10^4
对于60%以外另10%的数据,保证输入的a, b ∈ {0,1,2}
对于100%的测试数据,保证1 ≤ n, m ≤ 10^5,0a, b9,1 ≤ l ≤ r ≤ n

回滚莫队 O(10×n×n) 加卡常竟然能过……

首先先离线加排序。

对于 l,r 在一个分块,暴力求解。

对于 r,直接 O(1) 交换。

对于 l,利用回滚莫队,从前往后暴力更新即可。

最后处理先后顺序,从 l 滚到目标点的位置变化按照初始数组滚到 r 位置的序列的相对位置进行更新即可。

正解有点抽象,暂时先不讲了。

讲的有点少,因为基于理解莫队的人讲解,不理解的可以多理解下代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,m,k,sq[N],sl[N],sr[N],ans[N][10],num[10],p[N];
struct node
{
	int name,l,r;
}a[N];
int cmp(node fi,node se)
{
	if(sq[fi.l]==sq[se.l])return fi.r<se.r;
	return sq[fi.l]<sq[se.l];
}
inline void add(int x)
{
	swap(p[sl[x]],p[sr[x]]);
}
int main()
{
	freopen("niuniuball.in","r",stdin);
	freopen("niuniuball.out","w",stdout);
	scanf("%d%d",&n,&m);
	k=sqrt(n);
	for(int i=1;i<=n;i++)scanf("%d%d",&sl[i],&sr[i]),sq[i]=i/k;
	for(int i=1;i<=m;i++)scanf("%d%d",&a[i].l,&a[i].r),a[i].name=i;
	sort(a+1,a+1+m,cmp);
	a[0].l=n;
	int r;
	for(int i=1;i<=m;i++)
	{
		if(sq[a[i].l]!=sq[a[i-1].l])
		{
			r=(sq[a[i].l]+1)*k;
			for(int j=0;j<=9;j++)p[j]=j;
		}
		if(sq[a[i].l]==sq[a[i].r])
		{
			for(int j=0;j<=9;j++)ans[a[i].name][j]=j;
			for(int l=a[i].l;l<=a[i].r;l++)
			{
				swap(ans[a[i].name][sl[l]],ans[a[i].name][sr[l]]);
			}
			continue;
		}
		while(r<=a[i].r)add(r++);
		for(int j=0;j<=9;j++)num[j]=j;
		for(int l=a[i].l;l<=min(n,(sq[a[i].l]+1)*k-1);l++)
		{
			swap(num[sl[l]],num[sr[l]]);
		}
		for(int j=0;j<=9;j++)ans[a[i].name][j]=num[p[j]];
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=0;j<=9;j++)putchar(ans[i][j]+'0'),putchar(' ');
		putchar('\n');
	}
	return 0;
}

牛牛的凑数游戏
【题目描述】
对于一个多重数集S,对于一非负整数x,若存在S ⊆ S’ 且S’ 中所有数字之和
恰好等于x,则说S可以表示x。
显然对于任意的多重数集都可以表示 0,因为空集是所有集合的子集。
牛牛定义集合S的最小不能表示数为,一个最小的非负整数x, S不能表示x。
举个例子来说,例如S = {1,2,3,8,9},那么集合S的最小不能表示数就为 7。
因为子集的和为 0,子集1的和为 1,子集2的和为 2,子集1,2的和为 3,子集1,3
的和为 4,子集2,3的和为 5,子集1,2,3的和为 6。
但是无法找到子集权值和恰好为 7 的子集,所以 7 无法表示。
现在有一个长度大小为 n 的正整数数组,牛牛每次选择一个区间[l, r],他想要知
道假定给出的多重数集为{a_l
, a_l+1 … a_r}时,该集合的最小不能表示数是多少。
【输入格式】
第一行输入两个正整数 n,m。
接下来一行输入 n 个正整数ai表示输入的正整数数组。
接下来 m 行,每行输入两个正整数 l,r 表示查询的区间。
【输出格式】
对于每一个查询,输出最小不能表示数
【样例 1 输入】
8 6
1 2 3 4 5 17 1 99
1 5
2 5
1 6
1 7
1 8
3 8
【样例 1 输出】
16
1
16
34
34
2
【数据范围】
对于前10%的测试数据,保证1 ≤ n, m ≤ 10 ,1 ≤ ai ≤ 100
对于前20%的测试数据,保证1 ≤ n, m ≤ 500
对于另10%的测试数据,保证输入的ai单调非降
对于另10%的测试数据,保证输入的ai为 2 的非负整数幂。
对于100%的测试数据,保证1 ≤ n, m ≤ 10^5,1 ≤ ai ≤ 10^9 ,1 ≤ l ≤ r ≤ n

首先对于数 ai 和一个 sum 内被全部覆盖的区间,若 aisum,那么可以将区间覆盖到 ai+sum,经典背包,不证明。

那么通过这个可以推出来一种暴力做法,每一次找到一个在 lr 小于等于 sum 的数,将 sum 加上它,然后再找,初始 sum=1

但是这样复杂度明显会非常高,考虑优化这个过程。

利用树状数组维护 lr 的所有数,每次查询 sum 以内的所有数,进行累加,如果 sum 值发生了改变,再重复这个过程,直到不变为止。

可以很好的发现,每进行一次这个过程,sum 的值近似 ×2,所以总共最多进行 log(max(ai)) 次就可以更新完毕,我们称这种过程叫做迭代,迭代是因为处理顺序的原因导致一次答不出来最优解,就进行多次枚举得出答案的过程。

但是复杂度仍然不乐观,复杂度的根源在于每次建树状数组需要花费大量时间,所以考虑能否可持久化,那就可以打可持久化线段树,但是这里选择更简单的方法。

考虑对询问进行前缀和处理,每次需要查询的是 1r 小于等于 sum 的数与 1l1 小于等于 sum 的数。

那么有很多区间的询问是重合的,就可以离线处理了。

先每次标记询问的位置,然后从前往后遍历整个序列,维护树状数组,每次再更新询问所得到的值,结束遍历后用前缀和处理每次的答案,与之前答案作对比,如果被更新了,那么继续迭代,直到无数值更新后,答案就统计出来了。

时间复杂度:O(n×log(n)×log(max(ai)))

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m,cnt,f[N],t[N],ans[N],sum[N],bef[N];
struct node
{
	int name,data;
}a[N];
int cmp(node fi,node se)
{
	return fi.data<se.data;
}
int cmp2(node fi,node se)
{
	return fi.name<se.name;
}
struct node2
{
	int name,l,r,sum;
}p[N];
int cmp3(node2 fi,node2 se)
{
	return fi.r<se.r;
}
int lowbit(int x)
{
	return x&(-x);
}
void update(int x,int k)
{
	while(x<=cnt)
	{
		f[x]+=k;
		x+=lowbit(x);
	}
}
int search(int x)
{
	int sum=0;
	while(x)
	{
		sum+=f[x];
		x-=lowbit(x);
	}
	return sum;
}
vector<int>lc[N],rc[N];
void diedai()
{
	memset(f,0,sizeof(f));
	for(int i=1;i<=m;i++)sum[i]=1;
	bool flag=0;
	for(int i=1;i<=n;i++)lc[i].clear(),rc[i].clear();
	for(int i=1;i<=m;i++)lc[p[i].l-1].push_back(p[i].name);
	for(int i=1;i<=m;i++)rc[p[i].r].push_back(p[i].name);
	for(int i=1;i<=n;i++)
	{
		update(a[i].data,t[a[i].data]);
		int len=lc[i].size();
		for(int j=0;j<len;j++)
		{
			int l=1,r=cnt;
			while(l<r)
			{
				int mid=(l+r+1)>>1;
				if(t[mid]<=p[lc[i][j]].sum)l=mid;
				else r=mid-1;
			}
			sum[lc[i][j]]-=search(l);
		}
		len=rc[i].size();
		for(int j=0;j<len;j++)
		{
			int l=1,r=cnt;
			while(l<r)
			{
				int mid=(l+r+1)>>1;
				if(t[mid]<=p[rc[i][j]].sum)l=mid;
				else r=mid-1;
			}
			sum[rc[i][j]]+=search(l);
		}
	}
	for(int i=1;i<=m;i++)
	{
		bef[i]=p[i].sum;
		if(sum[i]!=p[i].sum)flag=1;
		p[i].sum=sum[i];
	}
	if(flag)diedai();
}
signed main()
{
	freopen("niuniunum.in","r",stdin);
	freopen("niuniunum.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i].data),a[i].name=i;
	sort(a+1,a+1+n,cmp);
	int bef=-1;
	for(int i=1;i<=n;i++)
	{
		if(a[i].data!=bef)cnt++,t[cnt]=a[i].data;
		bef=a[i].data;
		a[i].data=cnt;
	}
	sort(a+1,a+1+n,cmp2);
	for(int i=1;i<=n;i++)update(a[i].data,a[i].data);
	scanf("%lld",&m);
	for(int i=1;i<=m;i++)
	{
		p[i].name=i;
		scanf("%lld%lld",&p[i].l,&p[i].r);
		p[i].sum=1;
	}
	diedai();
	for(int i=1;i<=m;i++)printf("%lld\n",p[i].sum);
	return 0;
}
/*
8 6
1 2 3 4 5 17 1 99
1 5
2 5
1 6
1 7
1 8
3 8
*/

牛牛的 RPG 游戏
【题目描述】
牛牛最近在玩一款叫做“地牢迷宫”的游戏,该游戏中的每一层都可以看成是一个
n*m 的二维棋盘,牛牛从左上角起始的(1,1)点移动到右下角的(n,m)点。
游戏中的每一个格子都会触发一些事件,这些事件将会影响玩家的得分。
具体来说,每到一个格子玩家触发事件时,首先会立即获得一个收益得分 val(i,j)。
注意这个得分不一定是正的,当它的值为负时将会扣除玩家一定的分数。
同时这个事件还会对玩家造成持续的影响,直到玩家下一次触发其他事件为止,
每走一步,玩家都会获得上一个事件触发点 buff(i,j)的得分。
在游戏开始时牛牛身上还没有任何的 buff,所以在牛牛还未触发任何事件之前每
走一步都不会产生任何影响。
牛牛使用“潜行者”这个职业,所以他路过地牢中的格子时,可以选择不去触发这
些事件。
同时牛牛是一个速通玩家,想要快速的到达终点,所以他每次只会选择往右走或
者往下走。
牛牛想要知道,他玩游戏可以获得的最大得分是多少,你能告诉他么。
【输入格式】
第一行输入两个正整数 n,m
接下来输入 n 行,每行输入 m 个整数????????????????(????,????)表示该事件出发点被触发后,直
到下一次触发事件,每移动一步改变的得分。
接下来输入 n 行,每行输入 m 个整数????????????(????,????)表示该事件出发点被触发后,分数
的该变量。
输入保证,对于起点和终点,????????????(1,1) = ????????????(????, ????) = ????????????????(1,1) = ????????????????(????, ????) = 0。
【输出格式】
输出仅一行一个非负整数,表示牛牛从左上角走到右下角的最多得分。
【样例 1 输入】
3 3
0 1 -80
1 -1000 0
-100 0 0
0 -5 100
2 100 0
100 -1 0
【样例 1 输出】
20
【样例 1 说明】
一开始在(1,1)点,得分 0,身上没有事件影响????????????????。
接下来移动到(1,2)点,不触发事件,得分 0,身上没有事件影响????????????????。
接下来移动到(1,3)点,触发事件,得分 100,身上有????????????????影响,每走一步减少 80
点得分。
接下来移动到(2,3)点,移动时扣除 80 得分,身上还有 20 点得分,然后触发事件,
得分+0,同时 buff 被替换为每走一步+0。
接下来移动到(3,3)点,结束游戏,总得分 20

明人不说暗话,我不会。

posted @   Gmt丶Fu9ture  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示