bzoj 5418

3 篇文章 0 订阅
2 篇文章 0 订阅

这是拓展crt的典型应用

在你开始做之前,我一定要告诉你一件事情:虽然这道题看着和拓展crt模板很像,但他俩是有巨大的区别的!不要直接把板子改吧改吧扔上去!

题目模型:求解模线性方程组

\left\{\begin{matrix} m1*x & \equiv & a1 & mod &p1 \\ m2*x & \equiv & a2 & mod &p2 \\ ......\\ mn*xn &\equiv &an &mod &pn \end{matrix}\right.

其中p1,p2...pn不一定互质

第一眼:拓展crt板子题!

第二眼:等等...好像不太对

第三眼:WTF!系数哪来的!

我们知道,拓展crt的模板只能解决x系数为1的情况,而系数不为1的是很难做的!

什么?直接乘逆元变成1?

逆元不存在呢?

我们稍微做一点推导:

首先,我们解一下方程\begin{matrix} m1*x & \equiv & a1 & mod & p1 & & \end{matrix}

设这个方程的一个解是x0(这是可以使用拓展gcd求解的)

那么这个方程的通解应该是x=x0+k*(p/gcd(p,m)),k∈Z

那么这个通解等价于方程\begin{matrix} x & \equiv & x0 & mod & p/gcd(p,m) \end{matrix}的解

发现什么了吗?

是的!我们证明了方程\begin{matrix} m1*x & \equiv & a1 & mod & p1 & & \end{matrix}与方程\begin{matrix} x & \equiv & x0 & mod & p/gcd(p,m) \end{matrix}等价,这样就消掉了前面那个方程的系数!

所以,原方程组等价于这样:

\left\{\begin{matrix} x1 & \equiv & x0 & mod & p1/gcd(p1,m1)\\ x2 & \equiv & x0{}' & mod &p2/gcd(p2,m2) \\ ..... & & & & \\ xn & \equiv & x0{}'' &mod &pn/gcd(pn,mn) \end{matrix}\right.

这就很好了,我们使用正常的拓展crt解之即可

最后有几个细节问题:

①:对于ai>pi的情况,题目中给出的约束条件是pi=1,这样虽然拓展crt处理不了,但是我们可以应用特判过掉(p=1啊,多显然)

②:对于所有ai=pi的情况(即任一ai都=pi),只有当对应的攻击力是pi的倍数的时候才有解,否则无解,这个也要特判(有解也要特判,否则拓展crt解的结果会是0)

③:对于部分ai=pi的情况,如果对应攻击力不是pi的倍数则无解,但如果是pi的倍数,那么这个方程基本没用,可以替换成x\equiv 0 mod 1之类的形式

④:题目中运算很大,对于带取模的乘法需要快速加优化!同时所有数据类型建议使用long long以免挂掉

(求前驱那里本人使用的是treap,表示很好用)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define ls tree[rt].lson
#define rs tree[rt].rson
#define INF 0x3f3f3f3f
using namespace std;
ll n,m;
ll a[100005];
ll b[100005];
ll life[100005];
ll acc[100005];
ll p[100005];
ll s[100005];
int cyt=0;
int rot=0;
struct Treap
{
	int lson;
	int rson;
	int huge;
	int same;
	ll val;
	int rank;
}tree[200050];
void update(int rt)
{
	tree[rt].huge=tree[ls].huge+tree[rs].huge+tree[rt].same;
}
void lturn(int &rt)
{
	int temp=rs;
	rs=tree[rs].lson;
	tree[temp].lson=rt;
	tree[rt].huge=tree[temp].huge;
	update(temp);
	rt=temp;	
}
void rturn(int &rt)
{
	int temp=ls;
	ls=tree[ls].rson;
	tree[temp].rson=rt;
	tree[rt].huge=tree[temp].huge;
	update(temp);
	rt=temp;
}
void ins(int &rt,ll v)
{
	if(!rt)
	{
		rt=++cyt;
		tree[rt].huge=1;
		tree[rt].same=1;
		tree[rt].val=v;
		tree[rt].rank=rand();
		return;
	}
	if(v==tree[rt].val)
	{
		tree[rt].huge++;
		tree[rt].same++;
		return;
	}else if(tree[rt].val>v)
	{
		ins(ls,v);
		if(tree[ls].rank<tree[rt].rank)
		{
			rturn(rt);
		}
	}else
	{
		ins(rs,v);
		if(tree[rs].rank<tree[rt].rank)
		{
			lturn(rt);
		}
	}
}
void del(int &rt,ll v)
{
	if(!rt)
	{
		return;
	}
	if(tree[rt].val==v)
	{
		if(tree[rt].same>1)
		{
			tree[rt].huge--;
			tree[rt].same--;
			return;
		}else if(ls*rs==0)
		{
			rt=ls+rs;
			return;
		}else
		{
			if(tree[ls].rank<tree[rs].rank)
			{
				rturn(rt);
				del(rt,v);
			}else
			{
				lturn(rt);
				del(rt,v);
			}
		}
	}
	tree[rt].huge--;
	if(tree[rt].val>v)
	{
		del(ls,v);
	}else
	{
		del(rs,v);
	}
	update(rt);
}
void query_pro(int rt,ll v,int typ)
{
	if(!rt)
	{
		return;
	}
	if(tree[rt].val==v)
	{
		acc[typ]=v;
		return;
	}else if(tree[rt].val<v)
	{
		acc[typ]=tree[rt].val;
		query_pro(rs,v,typ);
	}else
	{
		query_pro(ls,v,typ);
	}
}
int query_min(int rt)
{
	if(ls&&tree[ls].val!=-INF)
	{
		return query_min(ls);
	}else if(tree[rt].val!=-INF)
	{
		return tree[rt].val;
	}else
	{
		return query_min(rs);
	}
}
ll pow_add(ll x,ll y,ll mod)
{
	ll ans=0;
	while(y)
	{
		if(y%2)
		{
			ans+=x;
			ans%=mod;
		}
		y/=2;
		x+=x;
		x%=mod;
	}
	return ans;
}
ll gcd(ll x,ll y)
{
	if(y==0)
	{
		return x;
	}
	return gcd(y,x%y);
}
void ex_gcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	ex_gcd(b,a%b,x,y);
	ll t=x;
	x=y;
	y=t-(a/b)*x;
}
bool makeit()
{
	for(int i=1;i<=n;i++)
	{
		if(acc[i]%p[i]==0)
		{
			if(p[i]==life[i])
			{
				a[i]=1;
				b[i]=0;
				continue;
			}else
			{
				printf("-1\n");
				return 1;
			}
		}
		ll x,y;
		ll r=gcd(acc[i],p[i]);
		if(life[i]%r)
		{
			printf("-1\n");
			exit(0);
		}
		acc[i]/=r;
		ll temp=life[i]/r;
		ll tt=p[i]/r;
		ex_gcd(acc[i],tt,x,y);
		x=(pow_add(x,temp,tt)+tt)%tt;
		b[i]=x;
		a[i]=tt;
	}
	return 0;
}
ll ex_crt()
{
	ll M0=a[1];
	ll ans=b[1];
	for(int i=2;i<=n;i++)
	{
		ll r=gcd(M0,a[i]);
		ll bb=((b[i]-ans)%a[i]+a[i])%a[i];
		if(bb%r)
		{
			return -1;
		}
		bb/=r;
		ll M=M0/r;
		ll aa=a[i]/r;
		ll x,y;
		ex_gcd(M,aa,x,y);
		x=pow_add(x,bb,aa);
		ans+=x*M0;
		M0*=aa;
		ans=(ans%M0+M0)%M0;
	}
	return (ans%M0+M0)%M0;
}
ll T;
int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		memset(tree,0,sizeof(tree));
		rot=0;
		cyt=0;
		ins(rot,-INF);
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&life[i]);//龙的生命力 
		}
		bool flag=0,flag1=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&p[i]);//龙的恢复力
			if(life[i]>p[i])
			{
				flag=1;
			}
			if(life[i]!=p[i])
			{
				flag1=1;
			}
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&s[i]);
		}
		for(int i=1;i<=m;i++)
		{
			ll x;
			scanf("%lld",&x);
			ins(rot,x);
		}
		for(int i=1;i<=n;i++)
		{
			acc[i]=0;
			query_pro(rot,life[i],i);
			if(acc[i]==-INF)
			{
				acc[i]=query_min(rot);		
			}
			del(rot,acc[i]);
			ins(rot,s[i]);
		}
		if(!flag1)
		{
			bool flag2=0;
			ll ans=1;
			for(int i=1;i<=n;i++)
			{
				if(life[i]%acc[i]!=0)
				{
					printf("-1\n");
					flag2=1;
				}else
				{
					ll cd=gcd(ans,life[i]/acc[i]);
					ans*=life[i]/acc[i]/cd;
				}
			}
			if(!flag2)
			{
				printf("%lld\n",ans);
			}
			continue;
		}
		if(flag)
		{
		    ll temp=0;
			for(int i=1;i<=n;i++)
			{
				if(life[i]%acc[i]!=0)
				{
					temp=max(temp,life[i]/acc[i]+1);
				}else
				{
					temp=max(temp,life[i]/acc[i]);
				}
			}
			printf("%lld\n",temp);
			continue;
		}
		if(makeit())
		{
			continue;
		}
		printf("%lld\n",ex_crt());
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值