S2考前综合刷题营Day6

math

题目描述

请求出 \(\gcd(q^a−1,q^b−1)\)\(p\) 的结果。

输入格式

第一行一个整数 \(T\),表示数据组数。

接下来 \(T\) 行,每行四个整数 \(q,a,b,p\)

输出格式

\(T\) 行,每行一个整数表示答案。

样例输入

1
3 1 2 11

样例输出

2

数据范围

对于 \(30\%\) 的数据,\(a,b≤500,T≤100\)
对于另外 \(30\%\) 的数据,\(p\) 为素数。

对于所有数据,满足 \(1≤q,a,b,p≤10^9,T≤10^4,q>1\)

Solution

老师做数竞时做到的一个有趣的结论,感觉放 \(NOIp T1(\)类似于 \(NOIp2017D1T1\)小凯的疑惑\()\)正合适,于是就出出来了。
一个数学结论:\(q^a-1|q^{ka}-1\)
\(\gcd(q^a-1,q^{ka+b}-1)\)
\(=\gcd(q^a-1,q^b-1+q^b(q^{ka}-1))\)
\(=\gcd(q^a-1,q^b-1)\)
通过辗转相除法最后可得到:\(q^{gcd(a,b)}-1\)

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
int T;
ll q,a,b,p,ans;
ll GCD(ll x,ll y)
{
	if(y==0) return x;
	return GCD(y,x%y);
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld%lld%lld",&q,&a,&b,&p);
		ll gcd=GCD(a,b);
		ans=1;
		while(gcd)       //快速幂求q^gcd 
		{
			if(gcd&1) ans=ans*q%p;
			q=q*q%p;
			gcd>>=1;
		}
		printf("%lld\n",(ans-1+p)%p);
	}
	return 0;
}

candy

题目描述
\(n\) 袋糖果,第 \(i\) 袋糖果有 \(B_i\) 颗糖。

\(Y\)想吃糖,共有 \(m\) 天,每天她会拿走糖果数\((\)\(B_j)\)\(A_i\) 的倍数的一袋糖果,满足 \(B_j\) 最小。

当然如果 \(B_j\) 不存在,她就没得吃了,因此她会很生气,从此不再吃糖,游戏也就结束了。

你可以帮小\(Y\)算出她能吃几天,以及每天吃了几颗吗?

输入格式

第一行,一个整数 \(n\)。接下来一个 \(n\) 个整数 \(B_i\)

下一行,一个整数 \(m\)。接下来一个 \(m\) 个整数 \(A_i\)

输出格式

第一行一个整数 \(k\) 表示天数。

接下来一行 \(k\) 个整数,表示前 \(k\) 天每天吃的糖果数量。

样例输入

4
1 6 8 16
4
4 2 4 5

样例输出

3
8 6 16

数据范围

对于 \(30\%\) 的数据,\(A_i≤10\)
对于另外 \(30\%\) 的数据,\(n,m≤5000\)
对于所有数据,\(1≤n,m,A_i,B_i≤10^6\)

Solution

题意就是对于 \(n\) 个数,你需要在一个数列里找到最小的是它倍数的数输出并将其删去。

60pts

直接模拟就是 \(O(n^2)\) 的算法,随便加点剪枝说不定能过\(2333\)

100pts

考虑到对于一个 \(A\),它所能找到的 \(B\) 一定是单调的,所以我们每次不需要从头开始扫描,可以用当前弧优化的思想:
用一个数组 \(cur[i]\) 记录 \(i\) 当前所能找到的最小的倍数是几,\(cnt[i]\) 表示 \(i\) 这个数在 \(B\) 数列中还有多少个。
对于一个 \(A\),若 \(cnt[cur[A]]>0\) ,说明最小倍数 \(cur[A]\) 还有,那么答案就是 \(cur[A]\);若 \(cnt[cur[A]]=0\),说明我们需要重新找倍数了,由于倍数具有单调性,我们可以让 \(cur[A]+=A\),如果在范围之内有 \(cnt[cur[A]]>0\) 那么说明就找到了,否则需要继续找。
这样做的复杂度是调剂函数的 \(O(n\log{n})\)

#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0'|ch>'9')ch= getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch= getchar ();
    return x;
}
const int N=1e6+5;
int n,m;
int a[N],b[N],cnt[N],cur[N],ans[N];
bool cmp(int x,int y)
{
	return x<y;
}
void print(int x)
{
	printf("%d\n",x);
	for(int i=1;i<=x;i++)
	    printf("%d ",ans[i]);
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++) 
	{
		b[i]=read();
		cnt[b[i]]++;      //记录每个数在B数组中出现多少次 
	}
	sort(b+1,b+1+n,cmp);  
	m=read();
	for(int i=1;i<=m;i++) 
	{
		a[i]=read();
		int x=cur[a[i]];  //当前的最小倍数 
		while(!cnt[x]&&x<=1e6) x+=a[i];  //如果没了就一直往下找 
		if(x>1e6)         //超出了数的最大范围,说明不存在倍数了 
		{
			print(i-1);
			return 0;
		}
		else              //如果找到了 
		{
			ans[i]=x;     
			cnt[x]--;
			cur[a[i]]=x;  //记录为当前的最新的倍数x 
		}
	}
	print(m);
	return 0;
}

lagrange

题目描述

你可能会很好奇题目名称有什么含义。不管怎样,考试结束之前,我是不会告诉你的。

你有 \(2\) 个长为 \(n\) 的序列 \(A_i,B_i\),需要支持 \(2\) 种操作,总共进行 \(q\)

查询区间 \(\sum_{1≤i<j≤r}(A_iB_j-A_jB_i)^2\) 的值,对 \(998244353\) 取模。

\((A_i,B_i)\) 修改为 \((x,y)\)

输入格式

第一行两个整数 \(n,q\)

接下来一行 \(n\) 个整数 \(A_i\)

接下来一行 \(n\) 个整数 \(B_i\)

接下来 \(q\) 行描述操作,操作有两种:

\(1\) \(l\) \(r\) 为操作 \(1\)

\(2\) \(i\) \(x\) \(y\) 为操作 \(2\)

输出格式

对于每次操作 \(1\),一行一个整数表示答案。

样例输入

3 3
1 2 3
3 2 1
1 1 3
2 2 6 1
1 1 3

样例输出

96
362

数据范围

对于 \(20\%\) 的数据,\(n,q≤200\)
对于 \(40\%\) 的数据,\(n,q≤2000\)
对于另外 \(30\%\) 的数据,只有操作 \(1\)
对于所有数据,满足 \(n,q≤500000,1≤A_i,B_i,x,y≤10^9\),其中 \(x,y\) 为操作 \(2\) 的参数。

Solution

20pts

直接暴力模拟。
时间复杂度 \(O(qn^2)\)

100pts

这种题不推式子没法做的。
有一个拉格朗日恒等式:
\((\sum_{i=1}^{n}a_i)^2(\sum_{i=1}^{n}b_i)^2=(\sum_{i=1}^{n}a_ib_i)^2+\sum_{1<=i<j<=n}(a_ib_j-a_jb_i)^2\)
所以说我们只需要维护 \(a_i\) 的平方和,\(b_i\) 的平方和,\(a_ib_i\) 的和,就可以 \(O(1)\) 求出答案了。发现这可以用树状数组或线段树维护。
时间复杂度 \(O(n\log{n})\)

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll read()
{
	char ch=getchar();
	ll a=0,x=1;
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') x=-x;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		a=(a<<1)+(a<<3)+(ch^48);
		ch=getchar();
	}
	return a*x;
}
const ll N=5e5+5;
const ll M=998244353;
ll n,m;
ll a[N],b[N],c[N];
struct node
{
	ll a,b,c;
	ll l,r;
}t[N<<2];
void update(ll node)
{
	t[node].a=(t[node<<1].a+t[node<<1|1].a)%M;
	t[node].b=(t[node<<1].b+t[node<<1|1].b)%M;
	t[node].c=(t[node<<1].c+t[node<<1|1].c)%M;
}
void build(ll node,ll l,ll r)
{
	if(l==r)
	{
		t[node].a=a[l]*a[l]%M;
		t[node].b=b[l]*b[l]%M;
		t[node].c=a[l]*b[l]%M;
		return ;
	}
	ll mid=(l+r)>>1;
	build(node<<1,l,mid);
	build(node<<1|1,mid+1,r);
	update(node);
}
ll queryA(ll node,ll l,ll r,ll x,ll y)
{
	if(x<=l&&r<=y) return t[node].a;
	ll mid=(l+r)>>1;
	ll cnt=0;
	if(x<=mid) cnt+=queryA(node<<1,l,mid,x,y);
	if(y>mid) cnt+=queryA(node<<1|1,mid+1,r,x,y);
	return cnt%M;
}
ll queryB(ll node,ll l,ll r,ll x,ll y)
{
	if(x<=l&&r<=y) return t[node].b;
	ll mid=(l+r)>>1;
	ll cnt=0;
	if(x<=mid) cnt+=queryB(node<<1,l,mid,x,y);
	if(y>mid) cnt+=queryB(node<<1|1,mid+1,r,x,y);
	return cnt%M;
}
ll queryC(ll node,ll l,ll r,ll x,ll y)
{
	if(x<=l&&r<=y) return t[node].c;
	ll mid=(l+r)>>1;
	ll cnt=0;
	if(x<=mid) cnt+=queryC(node<<1,l,mid,x,y);
	if(y>mid) cnt+=queryC(node<<1|1,mid+1,r,x,y);
	return cnt%M;
}
void insert(ll node,ll l,ll r,ll u,ll x,ll y)
{
	if(l==r)
	{
		t[node].a=x*x%M;
		t[node].b=y*y%M;
		t[node].c=x*y%M;
		return ;
	}
	ll mid=(l+r)>>1;
	if(u<=mid) insert(node<<1,l,mid,u,x,y);
	else insert(node<<1|1,mid+1,r,u,x,y);
	update(node);
}
int main()
{
	n=read();m=read();
	for(ll i=1;i<=n;i++) a[i]=read();
	for(ll i=1;i<=n;i++)
	{
		b[i]=read();
		c[i]=a[i]*b[i];
	}
	build(1,1,n);
	for(ll i=1;i<=m;i++)
	{
		ll opt=read();
		if(opt==1)
		{
			ll l=read();
			ll r=read();
			ll A=queryA(1,1,n,l,r);   //a_i的平方和 
			ll B=queryB(1,1,n,l,r);   //b_i的平方和 
			ll C=queryC(1,1,n,l,r);   //a_i*b_i的和 
			printf("%lld\n",((A*B%M-C*C%M)%M+M)%M); //利用拉格朗日恒等式求答案 
		}
		else
		{
			ll u=read();
			ll x=read();
			ll y=read();
			insert(1,1,n,u,x,y);      //相当于单点修改 
		}
	}
	return 0;
}

loop

题目描述

你有 \(m\) 个集合 \(P_i,Q_i\),满足 \(∀j∈P_i∪Q_i,j<i\)

现在你有一个 \(m\) 重循环,记第 \(i\) 重循环的变量为 \(x_i\),则 \(x_i\) i从 $\max{x_{P_i}} $ \(for\)\(\min{x_{Q_i}}\),特别地,\(P\) 为空集则 \(\max{x_{P_i}}=1\)\(Q\) 为空集则\(\min{x_{Q_i}}=n\)。(什么意思呢?就是说 \(∀j∈P_i,x_i≥x_j\),集合 \(Q\) 的意义同理可知)

你需要求出循环最内层里面的语句执行的次数,对 \(p\) 取模的结果。

输入格式

第一行三个正整数 \(m,n,p\)

接下来 \(m\) 行,每行输入了两个集合,每个集合的格式为:先输入一个数 \(l\) 表示集合大小,接下来输入 \(l\) 个元素。

输出格式

一行一个整数表示答案。

样例输入

2 10 13
0 0
1 1 0

样例输出

3

数据范围

对于 \(10\%\) 的数据,\(m≤6,n≤10\)
对于 \(30\%\) 的数据,\(m≤8\)
对于另外 \(30\%\) 的数据,\(|P_i|,|Q_i|≤1\)
对于所有数据,满足 \(m≤15,n,p≤10^9\),且 \(P_i,Q_i\) 中所有元素在 \([1,i−1]\) 中。

Solution

10pts

指数级别大爆搜。

100pts

我们只关心这 \(m\) 个变量的相对大小关系。
状压,从小到大加入值相同的一些数,同时可以判定是否合法。
\(dp[i][s]\) 表示目前从小到大加入了 \(i\) 种不同的数值,填了集合 \(s\) 的方案数。
\(dp\) 结束后用组合数算答案即可。
时间复杂度 \(O(n3^n)\)

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)x.size())
#define ALL(x) x.begin(),x.end()
#define L(i,u) for (register int i=head[u]; i; i=nxt[i])
#define rep(i,a,b) for (register int i=(a); i<=(b); i++)
#define per(i,a,b) for (register int i=(a); i>=(b); i--)
using namespace std;
typedef long double ld;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> Pii;
typedef vector<int> Vi;
template<class T> inline void read(T &x){
	x=0; char c=getchar(); int f=1;
	while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
	while (isdigit(c)) {x=x*10+c-'0'; c=getchar();} x*=f;
}
template<class T> T gcd(T a, T b){return !b?a:gcd(b,a%b);}
template<class T> inline void umin(T &x, T y){x=x<y?x:y;}
template<class T> inline void umax(T &x, T y){x=x>y?x:y;}
inline ui R() {
	static ui seed=416;
	return seed^=seed>>5,seed^=seed<<17,seed^=seed>>13;
}
//以上为yzr的火车头 
const int N = 1<<15|3;
int zuo[N],you[N],n,m,all,dp[N][17],mo;
void Get(int &res){int l,x;read(l);while(l--)read(x),res|=1<<x-1;}
inline void add(int &x, int y){x=x+y<mo?x+y:x+y-mo;}
int main() 
{
	read(n);read(m);read(mo);all=(1<<n)-1;
	rep(i,1,n)Get(zuo[1<<i-1]),Get(you[1<<i-1]);
	rep(s,0,all)rep(i,1,n)if(s>>i-1&1)zuo[s]|=zuo[1<<i-1],you[s]|=you[1<<i-1];
	dp[0][0]=1;
	rep(s,1,all)for(int t=(s-1)&s;;t=(t-1)&s)
	{
		if((zuo[s^t]&s)==zuo[s^t]&&(you[s^t]&t)==0)
			rep(i,0,n-1)add(dp[s][i+1],dp[t][i]);
		if(!t)break;
	}
	int res=0,cur=1;
	static int sta[233];int sz=0;
	rep(i,0,n)
	{
		cur=1;rep(j,1,sz)cur=1ll*cur*sta[j]%mo;
		add(res,1ll*dp[all][i]*cur%mo);
		sta[++sz]=m-i;
		int tmp=i+1;rep(j,1,sz){int g=gcd(tmp,sta[j]);tmp/=g;sta[j]/=g;}
		assert(tmp==1);
	}
	cout<<res<<endl;
	return 0;
}
posted @ 2020-10-06 19:14  暗い之殇  阅读(95)  评论(0编辑  收藏  举报