zoukankan      html  css  js  c++  java
  • [NOIP模拟测试38]题解

    来自达哥的问候……

    A.金

    显然本题的考察点在于高精而不是裴蜀定理

    根据裴蜀定理易得答案为Yes当且仅当$gcd(n,m)=1$,那么考虑怎么在高精度下判互质。

    如果$n,m$都能被2整除,那么显然不互质。

    如果其中一个可以而另一个不可以(以n能被2整除为例),$gcd(n,m)$就可以转化为$gcd(frac{n}{2},m)$

    如果两个数都不是2的倍数,根据更相减损术得到$gcd(n,m)=gcd(n,|n-m|)$

    重复这个过程即可。因为奇数减奇数一定是偶数,所以第三种操作不会连续进行两次,复杂度是log的。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=505;
    int T,p[N],q[N];
    ll n,m;
    ll gcd(ll x,ll y)
    {
        if(!y)return x;
        return gcd(y,x%y);
    }
    void print(int a[])
    {
        for(int i=a[0];i;i--)
            cout<<a[i];
        cout<<endl;
    }
    void ini(int a[])
    {
        char s[N];
        scanf("%s",s);
        a[0]=strlen(s);
        for(int i=1;i<=a[0];i++)
            a[i]=s[a[0]-i]-'0';
    }
    ll turnn(int a[])
    {
        ll res=0;
        for(int i=a[0];i;i--)
            res=res*10+a[i];
        return res;
    }
    int cmp(int a[],int b[])
    {
        if(a[0]>b[0])return 1;
        if(a[0]<b[0])return 0;
        for(int i=a[0];i;i--)
        {
            if(a[i]>b[i])return 1;
            if(a[i]<b[i])return 0;
        }
        return -1;
    }
    void jian(int a[],int b[])
    {
        int fl=cmp(a,b);
        if(fl==-1)
        {
            a[0]=0;
            return ;
        }
        if(fl==1)
        {
            for(int i=1;i<=a[0];i++)
            {
                if(a[i]<b[i])
                {
                    a[i+1]--;
                    a[i]+=10;
                }
                a[i]-=b[i];
            }
            while(a[0]>0&&a[a[0]]==0)a[0]--;
            return ;
        }
    }
    
    bool is2(int a[])
    {
        if(a[1]==2||a[1]==0||a[1]==4||a[1]==6||a[1]==8)return 1;
        return 0;
    }
    bool is3(int a[])
    {
        ll res=0;
        for(int i=a[0];i;i--)
            res+=a[i];
        if(res%3==0)return 1;
        return 0;
    }
    void divi(int a[],int b)
    {
        int x=0;
        for(int i=a[0];i;i--)
        {
            int old=(x*10+a[i])/b;
            x=(x*10+a[i])%b;
            a[i]=old;//cout<<a[i]<<endl;
        }
        while(a[0]>0&&a[a[0]]==0)a[0]--;
        return ;
    }
    bool leg(int a[],int b[])
    {
        while(a[0]&&b[0])
        {
            //print(a);print(b);
            //cout<<a[0]<<' '<<b[0]<<endl;
            if(is2(a)&&is2(b)){return 0;}
            if(is2(a))divi(a,2);
            else if(is2(b))divi(b,2);
            else
            {
                if(cmp(a,b))jian(a,b);
                else jian(b,a);
            }
    
        }
        if(a[0]==a[1]&&a[0]==1)return 1;
        if(b[0]==b[1]&&b[0]==1)return 1;
        return 0;
    }
    void qj1()
    {
    
        if(m==1)
        {
            puts("Yes");return ;
        }
        if(m==0||n==0)
        {
            puts("No");return ;
        }
        if(gcd(n,m)==1)
        {
            puts("Yes");
        }
        else puts("No");
        return ;
    
    }
    void work()
    {
        ini(p);ini(q);
        if(p[0]<=18&&q[0]<=18)
        {
            n=turnn(p);m=turnn(q);
            qj1();
            return ;
        }
        if(p[0]==1&&p[1]==0)
        {
            puts("No");
            return ;
        }
        if(q[0]==1&&q[1]==0)
        {
            puts("No");
            return ;
        }
        if(q[0]==1&&q[1]==1)
        {
            puts("Yes");
            return ;
        }
        if(q[0]==1&&(q[1]==2||q[1]==3))
        {
            m=turnn(q);
            if(m==2)
            {
                if(is2(p))puts("No");
                else puts("Yes");
            }
            if(m==3)
            {
                if(is3(p))puts("No");
                else puts("Yes");
            }
            return ;
        }
        if(leg(p,q))puts("Yes");
        else puts("No");
        return ;
    }
    int main()
    {
        //while(1)ini(p),divi(p,2),print(p),cout<<p[0]<<endl;
        scanf("%d",&T);
        while(T--)work();
        return 0;
    }
    

    B.斯诺

    首先考虑序列中只有0和1的情况,这时区间合法当且仅当区间中0的个数等于区间中1的个数。开桶维护,下标为区间中0的个数和1的个数的差值。直接利用前缀和扫一遍即可,复杂度$O(n)$,结合暴力可以得到60分。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=5e6+5;
    int n,a[N],sum[4][N];
    ll ans,cnt,bu1[N],bu2[N];
    char s[N];
    void qj1()
    {
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j++)
    		{
    			int len=j-i+1,ok=1;
    			for(int k=0;k<=2;k++)
    				if((sum[k][j]-sum[k][i-1])*2>len)
    				{
    					ok=0;break;
    				}
    			if(ok)ans++;
    		}
    	cout<<ans<<endl;
    }
    int main()
    {
    	scanf("%d",&n);
    	scanf("%s",s+1);
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=s[i]-'0';
    		for(int k=0;k<=2;k++)
    			sum[k][i]=sum[k][i-1]+(a[i]==k);
    		if(sum[0][i]==sum[1][i])cnt++;
    	}
    
    	if(n<=3000)
    	{
    		qj1();
    		return 0;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		ll m;
    		if(sum[0][i]>=sum[1][i])
    		{
    			ans+=bu1[sum[0][i]-sum[1][i]];
    			bu1[sum[0][i]-sum[1][i]]++;
    			if(sum[0][i]==sum[1][i])ans++;
    		}
    		else
    		{
    			ans+=bu2[sum[1][i]-sum[0][i]];
    			bu2[sum[1][i]-sum[0][i]]++;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    那么对于所有情况,显然区间中最多有1个数字数量超过一半,那么合法区间数就是总区间数($frac{n imes (n+1)}{2}$)减去0超过一半的区间数、1超过一半的区间数和2超过一半的区间数。

    对于每种数字维护前缀和,把多一个这种数字看作-1,多一个其它数字看作+1,那么问题转化为求前缀和数组的逆序对个数。可以上树状数组,注意序列要扫到0(因为前缀和的柿子是$sum[r]-sum[l-1]$)。另外,为了防止下标负数要集体加上一个值。复杂度$O(n log n)$,可以得到90分。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=5e6+5;
    int n,a[N],sum[3][N],c[3][N],minn[3]={0x3f3f3f3f,0x3f3f3f3f,0x3f3f3f3f};
    ll ans;
    char s[N];
    int lb(int x){return x&-x;}
    void add(int k,int x,int val)
    {
        for( ;x<=n;x+=lb(x))
            c[k][x]+=val;
    }
    ll query(int k,int x)
    {
        ll res=0;
        for( ;x;x-=lb(x))
            res+=c[k][x];
        return res;
    }
    void show(int k)
    {
        puts(" ");
        for(int i=1;i<=n;i++)
            cout<<sum[k][i]<<' ';
        puts(" ");
    }
    
    int main()
    {
        scanf("%d",&n);
        scanf("%s",s+1);
        for(int i=1;i<=n;i++)
        {
            a[i]=s[i]-'0';
            for(int k=0;k<=2;k++)
                sum[k][i]=sum[k][i-1]+(a[i]==k?-1:1),minn[k]=min(minn[k],sum[k][i]);
        }
        for(int k=0;k<=2;k++)
        {
            for(int i=0;i<=n;i++)
                sum[k][i]+=minn[k]<0?(-minn[k]+1):1;
            for(int i=n;i>=0;i--)
                ans+=query(k,sum[k][i]-1),add(k,sum[k][i],1);
            //cout<<ans<<endl;
            //show(k);
        }
        cout<<1LL*n*(n+1)/2-ans<<endl;
        return 0;
    }
    

    注意到这个前缀和数组有特殊性:相邻两项的差值最大为1。因此,相邻两项对答案的贡献也最多相差一种值的个数,开桶维护即可。复杂度$O(n)$。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=5e6+5;
    int n,a[N],sum[3][N],bu[3][N],minn[3]={0x3f3f3f3f,0x3f3f3f3f,0x3f3f3f3f};
    ll ans;
    char s[N];
    int main()
    {
        scanf("%d",&n);
        scanf("%s",s+1);
        for(int i=1;i<=n;i++)
        {
            a[i]=s[i]-'0';
            for(int k=0;k<=2;k++)
                sum[k][i]=sum[k][i-1]+(a[i]==k?-1:1),minn[k]=min(minn[k],sum[k][i]);
        }
        for(int k=0;k<=2;k++)
        {
            for(int i=0;i<=n;i++)
                sum[k][i]+=minn[k]<0?(-minn[k]+1):1;
            ll last=0;
            for(int i=n;i>=0;i--)
            {
                if(i!=n)
                {
                    if(sum[k][i]==sum[k][i+1]+1)ans+=last+bu[k][sum[k][i+1]],last+=bu[k][sum[k][i+1]];
                    else if(last&&sum[k][i]==sum[k][i+1]-1)ans+=last-bu[k][sum[k][i]],last-=bu[k][sum[k][i]];
                    else ans+=last;
                }
                bu[k][sum[k][i]]++;
            }
        }
        cout<<1LL*n*(n+1)/2-ans<<endl;
        return 0;
    }
    

    C.赤

    竟然是wqs二分套wqs二分,$n^3$硬生生砍成$n log^2 n$,太残暴了QAQ。

    首先暴力dp应该很好想,$dp[i][j][k]$表示遇到了i只猫,丢了j包干脆面和k包豆干。直接三层循环转移即可,边界稍微修剪一下可以得到50分。(然而考场上写着写着莫名其妙丢了一个方程)

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    int n,a,b;
    double dp[3][2005][2005],p[100005],q[100005];
    void work()
    {
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
            scanf("%lf",&p[i]);
        for(int i=1;i<=n;i++)
            scanf("%lf",&q[i]);
        double ans=0;
        //dp[1][1][0]=p[1];dp[1][0][1]=q[1];dp[1][1][1]=p[1]+q[1]-p[1]*q[1];
        int now=0,pre=1;
        for(int i=1;i<=n;i++)
        {
            now^=1,pre^=1;
            for(int j=0;j<=min(i,a);j++)
                for(int k=0;k<=min(i,b);k++)
                    dp[now][j][k]=0;
            for(int j=0;j<=min(i,a);j++)
            {
                for(int k=0;k<=min(i,b);k++)
                {
                    dp[now][j][k]=max(dp[pre][j][k],dp[now][j][k]);//就是它!
                    if(j>0)dp[now][j][k]=max(dp[pre][j-1][k]+p[i],dp[now][j][k]);
                    if(k>0)dp[now][j][k]=max(dp[pre][j][k-1]+q[i],dp[now][j][k]);
                    if(j>0&&k>0)dp[now][j][k]=max(dp[pre][j-1][k-1]+p[i]+q[i]-p[i]*q[i],dp[now][j][k]);
                    //cout<<i<<' '<<j<<' '<<k<<' '<<dp[now][j][k]<<endl;
                }
            }
        }
        for(int i=0;i<=min(n,a);i++)
            for(int j=0;j<=min(n,b);j++)
                ans=max(ans,dp[now][i][j]);
        printf("%.3lf
    ",ans);
    }
    int main()
    {
        while(scanf("%d%d%d",&n,&a,&b)!=EOF)
            work();
        return 0;
    }
    

    观察这个转移发现是很套路的wqs?不过辣鸡博主根本没写过wqs当然看不出来了。

    具体分析就直接放达哥官方题解好了(逃

    我们首先不考虑 b 的限制,假设豆干可以任意使用,定义 $f[i][j]$表示前 i 只猫使用 j 包干脆面
    和若干包豆干得到的最大收益。

    直接这么 DP 会 n 只猫都用豆干,可能会超出 b 的限制。


    为了减少豆干的使用,我们可以假定使用一个豆干需要额外付出 cost 的代价(也就是在转移
    的时候如果一只猫用了豆干,它对期望值的贡献要减去 cost)。

    在 DP 的时候,求解出 $f[i][j]$的最大值,并记录最优方案中豆干使用的数目 x,那么此时真实的期望是 $f[i][j]$的最优值加上
    $x*cost$,这个结果必然也是用了 x 个豆干时能够得到的最优结果。


    但是,如果我们随便假定一个 cost 去跑 DP,得到的方案并不一定把所有豆干都用完,我们需
    要一个能把所有豆干都用完的 cost 的值。


    显然豆干使用量随着 cost 的变化是单调变化的,我们可以二分 cost 的数值.

    这样dp数组就减少了一维,反映在时间复杂度上就是少了个n多了个log。同样地,我们还可以再砍掉一维,然后二分套二分解决。复杂度$O(n log^2 n)$。

    #define hzoj
    
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const double eps=1e-9;
    int n,a,b,na[100005],nb[100005];
    double dp[100005],p[100005],q[100005];
    int check(int op,double c1,double c2)
    {
        for(int i=1;i<=n;i++)
        {
            dp[i]=dp[i-1];
            na[i]=na[i-1];nb[i]=nb[i-1];
            if(dp[i-1]+p[i]-c1>dp[i])dp[i]=dp[i-1]+p[i]-c1,na[i]=na[i-1]+1,nb[i]=nb[i-1];
            if(dp[i-1]+q[i]-c2>dp[i])dp[i]=dp[i-1]+q[i]-c2,nb[i]=nb[i-1]+1,na[i]=na[i-1];
            if(dp[i-1]+p[i]+q[i]-p[i]*q[i]-c1-c2>dp[i])dp[i]=dp[i-1]+p[i]+q[i]-p[i]*q[i]-c1-c2,na[i]=na[i-1]+1,nb[i]=nb[i-1]+1;
        }
        if(op==1)return nb[n];
        else return na[n];
    }
    
    void work()
    {
    #ifdef luogu
        scanf("%d%d%d",&n,&a,&b);
    #endif
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
            scanf("%lf",&p[i]);
        for(int i=1;i<=n;i++)
            scanf("%lf",&q[i]);
        double l1=0.0,r1=1.0,l2,r2,mid1,mid2;
        while(r1-l1>eps)
        {
            mid1=(l1+r1)/2;
            l2=0.0,r2=1.0;
            while(r2-l2>eps)
            {
                mid2=(l2+r2)/2;
                if(check(1,mid1,mid2)>b)l2=mid2;
                else r2=mid2;
            }
            if(check(2,mid1,r2)>a)l1=mid1;
            else r1=mid1;
        }
        //check(0,r1,r2);
        printf("%.3lf
    ",dp[n]+1.0*b*r2+1.0*a*r1);
    }
    int main()
    {
    #ifdef hzoj
        while(scanf("%d%d%d",&n,&a,&b)!=EOF)
    #endif
            work();
        return 0;
    }
    
  • 相关阅读:
    Python3 简单实现下载某个网站轮播图片
    java中i=i++的解析
    String、StringBuffer、StringBuilder的区别和解析
    java值传递和引用传递
    Java并发(多线程)
    Java Set集合(HashSet、TreeSet)
    Java泛型的基本介绍与使用
    linux自学
    移动端项目准备工作
    响应式布局概念
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11479602.html
Copyright © 2011-2022 走看看