• 10.9 顾z校内互坑题


    T1 (help)

    题意简述

    给定一个长度为(n)的序列。然后给出多组询问.

    询问([l,r])区间内不等于该段区间(gcd)的数的个数。

    分析

      看到区间问题,优先考虑线段树 or 树状数组

    貌似可以树状数组做.但维护起来会比较麻烦.

    下面讲解线段树做法

    线段树做法.

    首先,线段树要维护区间(gcd)

    这里使用(tr[o])代表当前区间的区间(gcd).

    如何记录答案?

     很显然,我们直接记录([l,r])区间内不等于该段区间(gcd)的数的个数会比较麻烦.

    因此,考虑记录等于该段区间(gcd)的数的个数.(在线段树上维护.

    我们使用数组(sum[o])代表当前区间中等于区间(gcd)的数的个数.

    而每次询问的区间长度又已知,所以我们每一次询问的结果就是

    [ans=r-l+1-query(1,1,n,l,r,gd(1,1,n,l,r)); ]

    PS:(gd)函数是用来求解区间(gcd)

    如何更新/传递(sum)数组呢?

     首先,我们会把叶子节点的(sum)标记为(1)(只会有一个数啊 qwq.

    然后当向上传递的时候,我们的区间(gcd)会变化.

    这里写一下区间(gcd)的上传操作

    [tr[o]=gcd(tr[ls],tr[rs]); ]

    发现区间(gcd)是会变化的,因此我们的(sum)数组也要随之变化.

    我们需要判断的是(tr[o])的值是(tr[ls])还是(tr[rs])

    一.(tr[o])来自(tr[ls])

    此时我们的(sum[o])可以接受(sum[ls])的贡献,

    二.(tr[o])来自(tr[rs])

    此时我们的(sum[o])可以接受(sum[rs])的贡献.

    三. (tr[o])同时来自(tr[ls])(tr[rs])

    此时便可以同时获得贡献.

    因此,(sum)数组的转移就可以写成这样.

    [sum[o]=(tr[o]==tr[ls] ? sum[ls]:0)+(tr[o]==tr[rs] ? sum[rs]:0); ]

    此时,知道了(sum)数组,我们的问题就得以解决.

    代码

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #define N 5000008
    #define ls o<<1
    #define rs o<<1|1
    #define R register
    using namespace std;
    inline void in(int &x)
    {
    	int f=1;x=0;char s=getchar();
    	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    	x*=f;
    }
    int tr[N],sum[N],n,m;
    int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
    inline void up(int o)
    {
    	tr[o]=gcd(tr[ls],tr[rs]);
        sum[o]=(tr[o]==tr[ls] ? sum[ls]:0)+(tr[o]==tr[rs] ? sum[rs]:0);
    }
    void build(int o,int l,int r)
    {
        if(l==r)
        {
            in(tr[o]);
            sum[o]=1;
            return;
        }
        int mid=(l+r)>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        up(o);
        return;
    }
    int gd(int o,int l,int r,int x,int y)
    {
        if(x<=l and y>=r)return tr[o];
        int mid=(l+r)>>1;
        if(y<=mid) return gd(ls,l,mid,x,y);
        if(x>mid)	return gd(rs,mid+1,r,x,y);
        return gcd(gd(ls,l,mid,x,y),gd(rs,mid+1,r,x,y));
    }
    int query(int o,int l,int r,int x,int y,int k)
    {
        if(x<=l and y>=r) return tr[o]== k ?sum[o]:0;
        int mid=(l+r)>>1,res=0;
        if(y<=mid) return query(ls,l,mid,x,y,k);
        if(x>mid) return query(rs,mid+1,r,x,y,k);
        return query(ls,l,mid,x,y,k)+query(rs,mid+1,r,x,y,k);
    }
    int main()
    {
    	//freopen("help.in","r",stdin);
    	//freopen("help.out","w",stdout);
        in(n);
        build(1,1,n);
        in(m);
        for(R int l,r;m;m--)
        {
            in(l),in(r);
            printf("%d
    ",r-l+1-query(1,1,n,l,r,gd(1,1,n,l,r)));
        }
        fclose(stdin);
        fclose(stdout);
    	return 0;
    }
    

    T2(escape)

    题意简述

    给定(n)个数,这(n)个数均在(1-n)的范围内,其中有一个数出现了两次,其余数均出现了一次.让你求出这个出现两次的数.

    数据范围

    (2leq n leq10^7)

    时空限制

    (2000ms/16MB)

    吐槽

    如果数组给的足够大,相信大家都能切掉这种傻逼题.

    但是如果不能开数组呢?

    其实空间想给1MB来着,结果标程跑不动 QAQ

    这时候就需要用到我们的(Math)方法

    分析

     首先,从题目中大家应该都能得到一个信息.

    (n)(1-n)的数中,如果一个数出现了两次,且其他数都只出现了一次,那么必定有一个数没出现过.

    接下来进入数学推导时间.~w~

    数学推导

    我们设这个出现两次的数为(x),没有出现的数为(y),

    首先很明显的一个关系:

    [sum_{i=1}^{n}a_i -sum_{i=1}^{n}i=x-y. ]

    此方程左侧部分可求,如何求解右侧部分?

    明显,一个方程解两个未知数不显示.

    我们需要再找另一个关系.

    考虑到(sum),我们为何不考虑(prod)呢?

    手推一下可以发现一个新的关系(其实应该用不到手推) qwq

    [frac{prod_{i=1}^{n}a_i}{prod_{i=1}^{n}i}=frac{x}{y} ]

    然后解方程.

    [egin{cases}sum_{i=1}^{n}a_i -sum_{i=1}^{n}i=x-y\\frac{prod_{i=1}^{n}a_i}{prod_{i=1}^{n}i}=frac{x}{y}\end{cases} ]

    再把式子小小的变形一下.

    (sum_{i=1}^{n}a_i-sum_{i=1}^{n}i=d)

    (frac{prod_{i=1}^{n}a_i}{prod_{i-1}^{n}i}=p)

    [x-y=d\x=y+d ]

    推知

    [frac{y+d}{y}=p \ 1+frac{d}{y}=p ]

    建议手推,中间过程不继续放出.

    最终推知

    [y=frac{d}{p-1} ]

    因此

    [x= frac{d}{p-1}+d ]

    题目得解.

    由于(prod)操作会超级大.

    因此,此题为模意义下的解方程.

    代码

    PS:代码中的变量与推导部分的变量不符

    #include<cstdio>
    #include<cctype>
    #define mod 19260817
    #define R register
    using namespace std;
    inline long long ksm(long long x,long long y)
    {
    	long long res=1;
    	for(;y;y>>=1,x=x*x%mod)
    		if(y&1)res=res*x%mod;
    	return res;
    }
    long long n,k,suma,sum,paia=1,pai=1;
    long long c,d;
    int main()
    {
    	//freopen("escape.in","r",stdin);
    	//freopen("escape.out","w",stdout);
    	scanf("%lld",&n);
    	for(R int i=1;i<=n;i++)
    	{
    		R long long x;
    		scanf("%lld",&x);
    		(suma+=x)%=mod;
    		(sum+=i)%=mod;
    		(pai*=i)%=mod;
    		(paia*=x)%=mod;
    	}
    	c=suma-sum;
    	d=paia*ksm(pai,mod-2)%mod;
    	long long x=(c*ksm(d-1,mod-2)+c);
    	printf("%lld",(x%mod+mod)%mod);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    T3(cure)

    原题为(「JOI 2017 Final」)焚风现象

    题解你们如果看得懂日文就去看看 -->https://www.ioi-jp.org/joi/2016/2017-ho/2017-ho-t1-review.pdf

    分析

    首先,这是一个差分模板题,不过应该不是很容易看出来.

    涉及到了区间修改与单点查询.(我相信有人会写数据结构的.

    但是这里的单点查询就是一个固定的点(n)

    首先考虑为什么可以差分去做?

     题目中的描述就暗示着我们进行差分啊.

    首先我们发现了题目中的这一句话

    风的温度随海拔升降而变化。

    而,每个地点的海拔已知,所以我们可以在输入的时候求出每个位置的海拔差.

    做法

    根据海拔差,我们又可以求出每个位置的温度.由于(n)位置不会变,且一直为最后一个位置.

    所以我们可以累加(ans).(因为最后一个位置(n)永远不会变。

    而且考虑到某一段区间的海拔变化,相对位置不会变,我们只需要考虑从起始位置(l)的温度变化,后面到达(r)的温度就随之变化.

    这里需要注意的位置就是当(r==n)的时候,是不需要变化(r+1)位置的海拔与温度的.

    即我们的(ans)是不需要改变的.而改变的话,需要考虑相对高度的变化.我相信大家能懂的 qwq.

    代码

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #define N 200005
    #define R register
    using namespace std;
    long long n,q,s,t;
    long long A[N],last,ans;
    inline void in(long long &x)
    {
    	int f=1;x=0;char s=getchar();
    	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    	x*=f;
    }
    inline long long get(long long x)
    {
    	return  x > 0 ? -(x*s) : -(x*t) ; 
    }
    int main()
    {
    	//freopen("cure.in","r",stdin);
     	//freopen("cure.out","w",stdout);
    	in(n),in(q),in(s),in(t);
    	in(last);
    	for(R int i=1;i<=n;i++)
    	{
    		R long long x;
    		in(x);
    		A[i]=x-last;
    		last=x;
    		ans+=get(A[i]);
    	}
    	for(R long long x,y,z;q;q--)
    	{
    		in(x),in(y),in(z);
    		ans-=get(A[x]);
    		A[x]+=z;
    		ans+=get(A[x]);
    		if(y!=n) ans-=get(A[y+1]),A[y+1]-=z,ans+=get(A[y+1]);
    		printf("%lld
    ",ans);
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    还有标程很神奇

    #include<stdio.h>
    #include<vector>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    ll s1,s2;
    ll dif[300001];
    ll get(ll t)
    {
    	if(t>0)return -s1*t;
    	else return -s2*t;
    }
    int main()
    {
    	int num,query;
    	scanf("%d%d%lld%lld",&num,&query,&s1,&s2);
    	vector<int>v;
    	for(int i=0;i<=num;i++)
    	{
    		int z;
    		scanf("%d",&z);
    		v.push_back(z);
    	}
    	ll ans=0;
    	for(int i=0;i<num;i++)
    	{
    		dif[i]=v[i+1]-v[i];
    		ans+=get(dif[i]);
    	}
    	for(int i=0;i<query;i++)
    	{
    		int za,zb,zc;
    		scanf("%d%d%d",&za,&zb,&zc);
    		ans-=get(dif[za-1]);
    		dif[za-1]+=zc;
    		ans+=get(dif[za-1]);
    		if(zb!=num)
    		{
    			ans-=get(dif[zb]);
    			dif[zb]-=zc;
    			ans+=get(dif[zb]);
    		}
    		printf("%lld
    ",ans);
    	}
    }
    
    
  • 相关阅读:
    Vue 踩坑-2 vue文件中style的scoped属性
    IIS发布Vue项目F5刷新404问题
    .NET Core 3.1 + Hangfire 配置以及踩坑
    Vue 踩坑-1-跨域问题
    Docker 部署VUE项目
    (转)如何利用EnteLib Unity Interception Extension 和PIAB实现Transaction的Call Handler
    Unity 中的策略注入(转)
    面向方面的编程、侦听和 Unity 2.0(转)
    Unity 中的拦截功能(转)
    [转]推荐分享22个优秀的项目管理与协作工具
  • 原文地址:https://www.cnblogs.com/-guz/p/9759062.html
Copyright © 2020-2023  润新知