10.30 NOIP模拟赛(morning)

这里写图片描述

【问题描述】

从1−n中找一些数乘起来使得答案是一个完全平方数,求这个完全平方数
最大可能是多少。

【输入格式】

第一行一个数字n。

【输出格式】

一行一个整数代表答案对100000007取模之后的答案。

【样例输入】

7

【样例输出】

144

【样例解释】

但是塔外面有东西。

【数据规模与约定】

对于20%的数据,1<=n<=100。;
对于50%的数据,1<=n<=5000;
对于70%的数据,1<=n<=10^5;
对于100%的数据,1≤ n≤ 5*10^6;

思路

质因数分解,在1-n范围内的因数,偶数次幂的因数相乘,奇数次幂的因数的幂减一在相乘,得出的数就是ans
例如样例:
n=7,从2、3、4、5、6、7中选数
2=2^1;
3=3^1;
4=2^2;
5=5^1;
6=2^1*3^1;
7=7^1;
所以质因数2的幂是4,3的幂是2;
ans=2^4*3^2==144;

代码

#include<cstdio>
#include<iostream>
#define MAXN 5000010
#define MOD 100000007
#define ll long long
using namespace std;
ll prime[MAXN],tot,c[MAXN],n,ans=1;
bool b[MAXN];
inline void read(ll&x) {
    ll f=1;x=0;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=10*x+c-48,c=getchar();
    x=x*f;
}
inline void prim() {
    for(int i=2;i<=MAXN;i++) {
        if(!b[i]) prime[++tot]=i;
        for(int j=1;j<=tot&&i*prime[j]<=MAXN;j++) {
            b[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}
inline void ff() {
    for(int i=1;i<=tot;i++) {
        ll p=prime[i];
        if(p>n) break;
        while(n>=p) 
          c[i]+=n/p,p*=prime[i];
    }
}
inline ll quick_pow(ll a,ll b) {
    ll cnt=1;
    while(b) {
        if(b&1) cnt=(a*cnt)%MOD;
        a=(a*a)%MOD;
        b>>=1;
    }
    return cnt%MOD;
}
int main() {
    freopen("hao.in","r",stdin);
    freopen("hao.out","w",stdout);
    read(n);
    prim();
    ff();
    for(int i=1;i<=tot;i++)
      if(c[i]&1) c[i]--;
    for(int i=1;i<=tot;i++) {
        if(prime[i]>n) break;
        ans=(ans*quick_pow(prime[i],c[i]))%MOD;
    }
    printf("%d\n",ans);## 标题 ##
    fclose(stdin);
    fclose(stdout);
    return 0;
}

这里写图片描述

【问题描述】

有n个数,随机选择一段区间,如果这段区间的所有数的平均值在[?,?]中则
你比较厉害。求你比较厉害的概率。

【输入格式】

第一行有三个数n,l,r,含义如上描述。
接下来一行有n个数代表每一个数的值。

【输出格式】

输出一行一个分数 a/b代表答案,其中a,b互质。如果答案为整数则直接输出该
整数即可。

【样例输入 1】

4 2 3
3 1 2 4

【样例输出 1】

7/10

【样例输入 2】

4 1 4
3 1 2 4

【样例输出 2】

1

【样例解释】

塔外面有棵树。

【数据规模与约定】

对于30%的数据,1<=n<=10^4;
对于60%的数据,1≤n≤10^5 。
对于100%的数据,1 ≤n≤5*10^5 ,0<l≤r≤100。

思路:

此题乍一看,一脸懵逼,zhx讲后,原来是逆序对0.0.......
证明:
平均数=(ai+1,ai+2..ai+n)/n 
观察发现 ai数组 记前n项和(即a1+a2+a3..+an)为 An
那么要使平均数在 [l,r]内 那么 n*l<=An<=n*r
于是在区间[al,ar]中同时减去l,如果[al,ar]的和小于零,
则平均数不在[al,ar]内,大于零就在范围内。
同理[al,ar]区间减去r大于零就在范围内
(为什么可以这样大家自己YY一下就好了,还是比较显然的)
所以可以分别求一下ai数组减去 l和r 后前缀和,记为Si
当减去l时,如果Sb-Sa>0那么说明区间[a,b]的平均数在[l,r]内
变形可以得到Sb>Sa 
then于是逆序对就出来了.......T_T
当是减去r时正好相反,那么将sum反转一下就和减去l的情况一样了

真是太神辣!!
还要说一点就是这里用了补集转化的思想
我们求得是逆序对,可以发现逆序对是不合法的情况,
那么用总的情况减去不合法的情况就是合法情况了

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
int a[500100],b[500100],l,r,n;
LL sum[500100],c[500100],zi,mu,ans;
LL gcd(LL x,LL y)
{
    if(x<y)swap(x,y);
    return (!y)?x:gcd(y,x%y);
}
void merge(int l,int mid,int r)//mid为右区间左端点
{
    int i=l,j=mid,k=l-1;
    while((i<=mid-1)&&(j<=r))
    {
        if(sum[j]<sum[i])ans+=(LL)(mid-1-i+1);
        if(sum[j]<sum[i])c[++k]=sum[j++];
        else c[++k]=sum[i++];
    }
    while(i<=mid-1)c[++k]=sum[i++];
    while(j<=r)c[++k]=sum[j++];
    for(i=l;i<=r;i++)sum[i]=c[i];
}
void gb(int l,int r)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    gb(l,mid);gb(mid+1,r);
    merge(l,mid+1,r);
}
int main()
{
    freopen("jian.in","r",stdin);
    freopen("jian.out","w",stdout);
    int i,j,k;
    scanf("%d%d%d",&n,&l,&r);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    for(i=1;i<=n;i++) b[i]=a[i]-l;
    for(i=1;i<=n+1;i++) sum[i]=sum[i-1]+b[i-1];//sum要开LL   sum[j]>sum[i-1]
    ans=0;gb(1,n+1);
    zi+=ans;
    memset(sum,0,sizeof(sum));
    for(i=1;i<=n;i++) b[i]=a[i]-r;
    for(i=1;i<=n+1;i++) sum[i]=sum[i-1]+b[i-1];
    reverse(sum+1,sum+n+2);
    ans=0;gb(1,n+1);
    zi+=ans;
    mu=(LL)n*(LL)(n+1)/2;zi=mu-zi;//补集 
    if(zi==0)printf("0\n");
    else if(zi==mu)printf("1\n");
    else
    {
        LL d=gcd(zi,mu);
        zi/=d;mu/=d;
        cout<<zi<<'/'<<mu<<endl;
    }
    return 0;
}

这里写图片描述

【问题描述】

m×m的方阵上有n棵葱,你要修一些栅栏把它们围起来。一个栅栏是一段
沿着网格建造的封闭图形(即要围成一圈)。各个栅栏之间应该不相交、不重叠
且互相不包含。如果你最多修k个栅栏,那么所有栅栏的长度之和最小是多少?

【输入格式】

第一行三个整数m,k,n;
接下来n行每行两个整数x,y代表某棵葱的位置。

【输出格式】

一行一个整数代表答案。

【样例输入 1】

6 1 4
1 3
4 2
4 4
6 4

【样例输出 1】

18

【样例输入 2】

6 2 4
1 3
4 2
4 4
6 4

【样例输出 2】

16

【样例解释】

你猜树上有啥。

【数据规模与约定】

对于10%的数据,k=1。
对于30%的数据,k<=2;
对于60%的数据,k≤ 10。
对于100%的数据,1 ≤k≤n≤ 16,m≤ 1000。

思路:

思路是没有思路0.0
只有纯搜索
加卡时也只能搜到95分 没想到更好的方法

代码

#include<cstdio>
#include<iostream>
#include<ctime>
#include<cstring>
#include<cstdlib>
#define N 25
#define INF 1000000000
using namespace std;
int m,k,n,x[N],y[N],a[N],mnx[N],mxx[N],mny[N],mxy[N],ans=INF;
void dfs(int t)//枚举葱 
{
    if(clock()>=1950)
    {
        printf("%d",ans);exit(0);
    }
    if(t>n)
    {
        int sum=0;
        for(int i=1;i<=k;i++)
          if(a[i])sum+=(mxx[i]-mnx[i]+mxy[i]-mny[i]+2)*2;
        ans=min(ans,sum);
        return;
    }
    int sum=0;
    for(int i=1;i<=k;i++)
      if(a[i])sum+=(mxx[i]-mnx[i]+mxy[i]-mny[i]+2)*2;
    if(sum>=ans)return;
    for(int i=1;i<=k;i++)
    {
        int pmnx=mnx[i],pmxx=mxx[i],pmny=mny[i],pmxy=mxy[i];
        mnx[i]=min(mnx[i],x[t]);mxx[i]=max(mxx[i],x[t]);
        mny[i]=min(mny[i],y[t]);mxy[i]=max(mxy[i],y[t]);
        a[i]++;
        dfs(t+1);
        mnx[i]=pmnx,mxx[i]=pmxx,mny[i]=pmny,mxy[i]=pmxy;
        a[i]--;
    } 
}
int main()
{
    //freopen("dan.in","r",stdin);
    //freopen("dan.out","w",stdout);
    memset(mnx,0x3f3f3f3f,sizeof(mnx));
    memset(mny,0x3f3f3f3f,sizeof(mny));
    scanf("%d%d%d",&m,&k,&n);
    for(int i=1;i<=n;i++)
      scanf("%d%d",&x[i],&y[i]);
    dfs(1);
    printf("%d",ans);
    fclose(stdin);fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值