123789456ye

已AFO

毒瘤xgzc的数论系列题解

A 摆干草

  题意:有一个 1~n 的排列,q 个陈述,每次给出 l~r 的区间最小值 a,问第一个与前面矛盾的陈述序号。
  \(1\leq n\leq 10^6,q\leq 25000\)
  题解:二分答案。若某些 a 较大的区间的交集包含了某些 a 较小的区间的并集则矛盾。
  包含关系可以用并查集或线段树(并查集真毒瘤啊)
  线段树不多讲。并查集的话若 [l,r] 被覆盖则暴力将区间的父节点指向 r+1。如果 find(l)>r 则证明区间被覆盖过了。(好玄学啊) (可以证明均摊复杂度很小 from xgzc)
  复杂度:\(O(q\log q*\alpha(n))\)

#include<bits/stdc++.h>
using namespace std;
#define maxn 25005
struct Query
{
	int l,r,v;
	inline friend bool operator < (Query a,Query b)
		{
			return a.v>b.v;
		}
}query[maxn],tmp[maxn];
int fa[1000005],n;
inline int findf(int x)
{
	return (fa[x]==x)?x:(fa[x]=findf(fa[x]));
}
bool check(int x)
{
	for(int i=1;i<=x;++i) tmp[i]=query[i];
	for(int i=1;i<=n+1;++i) fa[i]=i;
	sort(tmp+1,tmp+x+1);
	int l1,l2,r1,r2;
	l1=l2=tmp[1].l,r1=r2=tmp[1].r;
	for(int i=2;i<=x;++i)
	{
		if(tmp[i].v<tmp[i-1].v)
		{
			if(findf(l1)>r1) return false;
			for(int j=fa[l2];j<=r2;j=fa[j])
			{
				findf(j);
				fa[j]=findf(j+1);
			}
			l1=l2=tmp[i].l,r1=r2=tmp[i].r;
		}
		else
		{
			l1=max(l1,tmp[i].l),l2=min(l2,tmp[i].l);
			r1=min(r1,tmp[i].r),r2=max(r2,tmp[i].r);
			if(r1<l1) return false;
		}
	}
	if(findf(l1)>r1) return false;
	return true;
}
int main()
{
	freopen("bales.in","r",stdin),freopen("bales.out","w",stdout);
	int q;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=q;++i)
		scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].v);
	int l=1,r=q+1,mid;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(check(mid)) l=mid+1;
		else r=mid;
	}
	printf("%d\n",l>q?0:l);
	return 0;
}

B saber

  题意:给定\(n* m\)网格图,\(y=x+1\)左上所有点不能走(线上可以),只能向上或向右,问从(0,0)到(n,m)路径条数模19999999。\(2\leq m\leq n\leq 10^{10}\)
  题解:考虑不合法方案数。显然所有这种路径,都可以将线\(y=x+2\)外的路径沿线从右下折叠到左上,也就是从(0,0)到(m-2,n+2)的方案数。所以\(ans=C_{n+m}^{m}-C_{n+m}^{m-2}\)
  由于模数较小,上lucas即可。
  复杂度:\(O(mod)\)

#include<bits/stdc++.h>
using namespace std;
#define mod 19999999
#define ll long long
ll inv[mod+5],fac[mod+5];
inline void exgcd(ll a,ll b,ll& x,ll& y)
{
	if(!b)
	{
		x=1,y=0;
		return;
	}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}
inline void pre(int x)
{
	inv[0]=fac[0]=1;
	for(int i=1;i<=x;++i)
		fac[i]=fac[i-1]*i%mod;
	ll tx,ty;
	exgcd(fac[x],mod,tx,ty);
	inv[x]=(tx%mod+mod)%mod;
	for(int i=x-1;i;--i)
		inv[i]=(i+1)*inv[i+1]%mod;
}
inline ll c(ll x,ll y)
{
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
inline ll lucas(ll x,ll y)
{
	return c(x%mod,y%mod)*c(x/mod,y/mod)%mod;
}
int main()
{
	freopen("saber.in","r",stdin),freopen("saber.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	pre((n+m+2)%mod);
	printf("%lld\n",(lucas(n+m,m)-lucas(n+m,m-2)+mod)%mod);
	return 0;
}

C 不够优秀

  题意:龙刷到了一道题。他不会做。
  给定n,求
  $$\sum_{i=1}^{n} \sum_{j=1}^{n} \sum_{k=1}^{n} [j\mid i][(j+k)\mid i] \mod 998244353$$
  其中\([x]\)表示若 x 为真则值为1否则为0。\(n\leq 10^7\)
  题解:显然\(ans=\sum_{i=1}^{n}{\frac{d(i)^2-d(i)}{2}}\),其中 d 为因数个数函数。
  线性筛即可。
  复杂度:\(O(n)\)

#include<bits/stdc++.h>
using namespace std;
#define mod 998244353
#define maxn 10000005
int n,vis[maxn],id[maxn],num[maxn],prime[1000005],cnt;
inline void eular(int x)
{
	int tmp;
	for(int i=2;i<=x;++i)
	{
		if(!vis[i]) prime[++cnt]=i,id[i]=2,num[i]=1;
		for(int j=1;j<=cnt&&i*prime[j]<=x;++j)
		{
			tmp=i*prime[j];
			vis[tmp]=1;
			if(i%prime[j])
			{
				id[tmp]=2*id[i];
				num[tmp]=1;
			}
			else
			{
				id[tmp]=id[i]*(num[i]+2)/(num[i]+1);
				num[tmp]=num[i]+1;
				break;
			}
		}
	}
}
int main()
{
	freopen("good.in","r",stdin),freopen("good.out","w",stdout);
	scanf("%d",&n);
	eular(n+2);
	long long ans=0;
	int tmp;
	for(int i=1;i<=n;++i)
	{
		tmp=id[i];
		ans+=((tmp*tmp-tmp)>>1)%mod;
		ans%=mod;
	}
	printf("%lld\n",ans);
	return 0;
}

D 闷声大发财

  题意:龙和hyj比赛做题。有 n-1 道题,编号为 2~n 。每道题只能由一个人做(或者没人做)。求从龙与hyj做的题中任意分别选两个数都不互质的方案数。两个人都可以不做题。方案数对 p 取模。\(n\leq 500,p\leq 10^9\)
  题解:状压\(\sqrt n\)内的质数集合。设\(dp[i][j]\)表示龙选的数的质因子集合为 i ,hyj选的为 j 。这样(可能)会剩一个大质数。设\(f1[i][j][k],f2[i][j][k]\)表示选到第i个,两个集合为 j,k ,大质数被某个人选的方案数。
  两个数组初值为dp。
  转移为\(f1[i][j\mid s][k]+=f1[i][j][k](s\&k==0)\)。另一个同理。
  \(ans=\sum_{i=0}\sum_{j=0}{dp[i][j]}\)
  注意要把 i 给滚掉,否则会MLE。
  复杂度:\(O(n*2^8*2^8)\) (因为比\(\sqrt n\)小的第一个质数是19,第8个)

#include<bits/stdc++.h>
using namespace std;
struct Num
{
	int s,num;
	inline friend bool operator < (Num a,Num b)
		{
			if(a.num==b.num) return a.s<b.s;
			return a.num<b.num;
		}
}num[505];
int prime[9]={0,2,3,5,7,11,13,17,19};
int n,p,dp[257][257],tmp[2][257][257],ans;
inline void add(int& a,const int& b)
{
	a=(a+b)%p;
}
int main()
{
	freopen("rich.in","r",stdin),freopen("rich.out","w",stdout);
	scanf("%d%d",&n,&p);
	int tn;
	for(int i=2;i<=n;++i)
	{
		tn=i;
		for(int j=1;j<=8;++j)
		{
			if(tn%prime[j]) continue;
			num[i].s|=(1<<(j-1));
			while(!(tn%prime[j])) tn/=prime[j];
		}
		num[i].num=tn;
	}
	sort(num+2,num+n+1);
	dp[0][0]=1;
	for(int i=2;i<=n;++i)
	{
		if(i==2||num[i].num==1||num[i].num xor num[i-1].num)
			memcpy(tmp[1],dp,sizeof(dp)),memcpy(tmp[0],dp,sizeof(dp));
		for(int j=255;j>=0;--j)
			for(int k=255;k>=0;--k)
			{
				if(!(k&num[i].s)) add(tmp[0][j|num[i].s][k],tmp[0][j][k]);
				if(!(j&num[i].s)) add(tmp[1][j][k|num[i].s],tmp[1][j][k]);
			}
		if(i==n||num[i].num==1||num[i].num xor num[i+1].num)
			for(int j=0;j<=255;++j)
				for(int k=0;k<=255;++k)
					dp[j][k]=((tmp[0][j][k]+tmp[1][j][k]-dp[j][k])%p+p)%p;
	}
	for(int i=255;i>=0;--i)
		for(int j=255;j>=0;--j)
			if(!(i&j)) add(ans,dp[i][j]);
	printf("%d\n",ans);
	return 0;
}

以上为考试题

讲的题都不会写qaq


posted @ 2019-09-15 10:43  123789456ye  阅读(15)  评论(1)    收藏  举报