数论小训练

A题
意思就是给你一个序列a 含有n个元素 和一个数x 然后你需要做的是 在1–x这个段里面减去a序列里面存在的小于等于x 的数 ,求和 大概就是这个意思,很简单

B题 一开始一个序列的值都是相等的 然后他有一种变化值的规则
在这里插入图片描述
现在问我们对序列操作若干次后 求和的最大值模1e9+7是多少
emmm这题其实我是推了几个小样例找规律的
其实是程 首项为初始值 公比为2的等比数列分布
求和一次就好了

C题 要你在n个数中找一段连续的数 使得gcd这个段最大 ,然后问你最大值和这个最大的集合有多大 ,显然,因为题目要我们找的是连续的段,而且题面优先保证gcd最大 其次是集合最大 那么我们只需要一开始连续gcd找出来这个最大值 然后再寻找一个连续段的最大长度 都满足a_i%gcdmax =0 即可

D题 给出一个序列a 长度为n 现在要你重新排列使得 数列程这种 分布
在这里插入图片描述
问组合的方案数模p 是多少
首先我们要分析,因为他是严格递增或者递减 或者严格递增后再递减
那么显然易得 一个数最多只能出现两次,但是特别的,最大的那个数只能允许出现一次 因为他的峰 显然其他出现两次的数只能放在峰的两边 那么他们对方案数其实是恒定为1的贡献的 我们也不需要计算它的贡献
然后考虑有多少个只出现过一次的的数 首先我们一开始是n个数,通过上述条件判断了否定情况后,峰值一定是一个而且我们不能动它了 那么 n=n-1 然后我们还有cnt个出现次数为2的数 也不能去动它 那么n=n-2*cnt
剩下的数 我们可以随意的分布在左边 若干个 右边若干个
那么相当于是对于组合数求一次 从0 到n(更新后的n) 的组合数值

 #include<bits/stdc++.h>
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll long long
#define int long long
#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
 using namespace std;
 ll gcd(ll a,ll b){if(a<0)a=-a;if(b<0)b=-b;return b==0?a:gcd(b,a%b);}
template<typename T>void read(T &res){bool flag=false;char ch;while(!isdigit(ch=getchar()))(ch=='-')&&(flag=true);
for(res=ch-48;isdigit(ch=getchar());res=(res<<1)+(res<<3)+ch - 48);flag&&(res=-res);}
ll lcm(ll a,ll b){return a*b/gcd(a,b);}
ll qp(ll a,ll b,ll mod){ll ans=1;if(b==0){return ans%mod;}while(b){if(b%2==1){b--;ans=ans*a%mod;}a=a*a%mod;b=b/2;}return ans%mod;}//快速幂%
ll qpn(ll a,ll b, ll p){ll ans = 1;a%=p;while(b){if(b&1){ans = (ans*a)%p;--b;}a =(a*a)%p;b >>= 1;}return ans%p;}//逆元   (分子*qp(分母,mod-2,mod))%mod;


ll a[111111];
map<ll,ll>s;
set<ll>sb;
const int Max = 1e6 + 10;
ll fact[Max], ifact[Max];		//fact[i]是i的阶乘,ifact[i]是阶乘的除法逆元,两者用于求组合数

void init()		//初始化
{
	fact[0] = ifact[0] = 1;
	for (int i = 1;i < Max; i++)
	{
		fact[i] = (fact[i - 1] * i) % mods;
		ifact[i] = qp(fact[i], mods - 2,mods);
	}
}

ll C(ll n, ll m)
{
	if (n < m || m < 0) return 0;		//不合法
	return (fact[n] * ifact[m] % mods) * ifact[n - m] % mods;
}
signed main(){

init();
ll t;
read(t);
while(t--){
    s.clear();
    sb.clear();
   ll n;
   read(n);
   ll ma=0;
   ll cnt=0;
   ll f=0;
   for(int i=1;i<=n;i++){
    read(a[i]);
  //  printf("?????\n");
    ma=max(ma,a[i]);
    s[a[i]]++;
    if(s[a[i]]>=3){
    f=1;
    }
   }
   for(int i=1;i<=n;i++){
    if(s[a[i]]==2){
      sb.insert(a[i]);
    }
   }
    if(f||s[ma]>=2){
        printf("0\n");
        continue;
    }
   cnt=sb.size();
   ll rnm=(n-1)-2*cnt;  // 剩余可以自己调用
  // printf("%lld\n",rnm);
   ll cnm=0;
  for(int i=0;i<=rnm;i++){
   cnm=(cnm+C(rnm,i))%mods;
  }
  printf("%lld\n",cnm%mods);
}
return 0;

}

E 题 就是给了我们一个数 和三种情况的价值 三种情况分别对应这个数的因子的种类情况,但是这个因子不能是本身,现在问我们 能求的最小值是多少,三种情况 分别是a b c 值 加上一个X/因子
首先分析 因子为质因子,那么要值尽可能小 我们就要拿它的最大质因子去除,才是此类情况的最小值
然后分析合数因子,一个数如果只有两个质因子相乘组合 那么它没这种情况,否则我们只需要除掉它的 X/min_质因子 也就是它的最大合数因子
还有一种情况就是X本身为质数 我们直接求c+X
综上所述以上三种情况取min 但是x范围1e7 我们需要用到最牛逼的质因子分解才不能TLE (我反正TLE+WA 31发才过)

#include<bits/stdc++.h>
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll long long
#define int long long
#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
 using namespace std;
/*
const int maxn=1e7+300;
typedef long long LL;
LL v[maxn],primes[maxn];//v[i]存的是i的最小质因子,primes[m]里面存筛出来的质数
LL m=0;//质数数量
void getprimes(int n)
{
	//memset(v,0,sizeof(v));//最小质因子

	for(LL i=2;i<=n;i++)
	{
		if(v[i]==0) {v[i]=i;primes[++m]=i;}//i是质数
		//给当前的数i乘上一个质因子
		for(LL j=1;j<=m;j++)
		{
			//i有比primes[j]更小的质因子,或者超出n的范围,停止循环
			if(primes[j]>v[i]||primes[j]>n/i)	break;
			//primes[j]是合数i*primes[j]的最小质因子
			v[i*primes[j]]=primes[j];
		}
	}
//	for(LL i=1;i<=m;i++) cout<<primes[i]<<endl;
}



*/
bool is_prime(long long x){
    if(x==1)
        return false;
    if(x==2||x==3)
        return true;
    if(x%6!=1&&x%6!=5)
        return false;
    int s=sqrt(x);
    for(int i=5;i<=s;i+=6)
        if(x%i==0||x%(i+2)==0)
            return false;
    return true;
}

vector<long long> pfc(long long n){//快速质因数分解
	vector <long long> st;
	long long i=0;
	if(n==1)
	{
		st.push_back(1);
		return st;
	}
	while(i<n)
	{
		if(is_prime(n)){
			st.push_back(n);
			return st;
		}
		for(i=2;i<n;i++)
		{
			if(n%i==0)
			{
				st.push_back(i);
				n/=i;
				break;
			}

		}
	}
	st.push_back(n);
	return st;
}
vector<ll>sb;
signed main(){
  // get_pri();
//  getprimes(10000010);
   ll t;
   scanf("%lld",&t);
    while(t--){
    ll x;
    ll a,b,cc;
    scanf("%lld",&x);
    scanf("%lld",&a);
     scanf("%lld",&b);
     scanf("%lld",&cc);
     sb=pfc(x);
    // for(int i=0;i<sb.size();i++){
    //    printf("Q %lld\n",sb[i]);
   //  }
  //   sb.clear();

    ll mafac=sb[sb.size()-1];
    ll f=sb[0];
    ll ve;
    ll ve_;
    ll _ve;
    if(mafac==x){
    printf("%lld\n",cc+x);
    continue;
    }

  //  printf("Q %lld %lld\n",mafac,f);
    ve=a+x/mafac;
    ve_=b+f;
    _ve=cc+x;
    if(sb.size()==2){
        ve_=1111111111;
    }

    printf("%lld\n",min(min(_ve,ve),ve_));
    }
}

F 偷懒 跑路
G 题 给出一个序列a 长度为n
找出一段连续的相乘的值模n等于1的最小长度
是不是很懵逼 ??
我们用map标记
当ai与n不互质时,重置累乘结果以及乘法起点 因为这一段乘法模n不是整除,我们只有当ai与n互质才能往里面加,否则容易形成n 的倍数 显然我们想要获得一段 累乘%n=1 要么这个数%n=1 那么就是一段数的累乘取模结果出现了两次

#include<bits/stdc++.h>

typedef long long ll;
typedef long long LL;
typedef long double ld;
using namespace std;
mt19937_64 mrand(chrono::steady_clock::now().time_since_epoch().count());
//mt19937_64 mrand(42);
#define ii for(int i=1;i<=n;++i)
#define ji for(int j=1;j<=n;++j)
#define jj for(int j=1;j<=m;++j)
#define ij for(int i=1;i<=m;++i)
#define sz(x) ((ll)x.size())
#define all(x) x.begin(),x.end()
#define al(x) x+1,x+1+n
#define asd cout<<"ok"<<endl;
#define asdd cout<<"okok"<<endl;
#define vi vector<int>
#define vvi vector<vector<int>>
#define vl vector<ll>
#define vii vector<pair<int,int>>
#define pr(v) for(auto i:v) cout<<i<<" ";cout<<endl;
#define prt(a, l, r) for(auto i=l;i<=r;++i) cout<<a[i]<<" ";cout<<endl;
#define pc(x) __builtin_popcount(x)
#define pb push_back
#define PS string qqwwee;cin>>qqwwee;
typedef pair<int,int> pii;

const int maxn = 100005;
ll a[maxn],v[maxn];
int gcd(int a,int b) {return b==0?a:gcd(b,a%b);}
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
    if(!b) {d=a;x=1;y=0;}
    else {exgcd(b,a%b,d,y,x);y-=x*(a/b);}
}
ll inv(ll a,ll p) {
    ll d,x,y;
    exgcd(a,p,d,x,y);
    assert(d == 1);
    x%=p;
    if(x<0) x+=p;
    return x;
}

int main() {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    while(t--) {
        int n;
        cin>>n;
        for(int i=1;i<=n;++i) cin>>a[i];
        unordered_map<int,int> mp;
        mp[1]=0;
        int ans=n+1;
        ll s=1;
        for(int i=1;i<=n;++i) {
            if(gcd(a[i],n) != 1) {
                s=1;
                mp.clear();
                mp[1]=i;
            } else {
                s=s*a[i]%n;
                if(mp.count(s)) ans=min(ans,i-mp[s]);
                mp[s]=i;
            }
        }
        if(ans>n) ans=0;
        cout<<ans<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值