zoukankan      html  css  js  c++  java
  • 【2018.11.7】luogu NOIp热身赛 及刷题思考

    A. 区间方差

    ——线段树

    其实这是一道题的弱化版

    P1471 方差

    本题只涉及单点修改和区间查询

    不用懒标记,直接修改单点

    重要的是方差的计算

    (s^2=frac{(a_1-overline{a})^2+(a_2-overline{a})^2+(a_3-overline{a})^2…+(a_n-overline{a})^2}{n}=)

    =(frac{noverline{a}^2-2overline{a}(a_1+a_2+a_3+…a_n)+a_1^2+a_2^2+a_3^2+…+a_n^2}{n})

    (ecause frac{a_1+a_2+a_3+…+a_n}{n}=overline{a})

    ( herefore s^2=frac{a_1^2+a_2^2+a_3^2+…+a_n^2}{n}-overline{n}^2)

    因此我们只需维护区间和以及区间平方和就能顺利计算出方差

    #define lson o<<1
    #define rson o<<1|1
    const int N=1e5+10,mod=1e9+7;
    
    struct tree{
    	int l,r;
    	int sum,sum_2;
    }t[N<<2];
    
    int n,m;
    int val[N];
    
    inline void pushup(int o){
    	t[o].sum=(t[lson].sum+t[rson].sum)%mod;
    	t[o].sum_2=(t[lson].sum_2+t[rson].sum_2)%mod;
    }
    
    inline void build(int o,int l,int r){
    	t[o].l=l,t[o].r=r;
    	if(l==r){
    		t[o].sum=val[l];
    		t[o].sum_2=1ll*val[l]*val[l]%mod;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(lson,l,mid);
    	build(rson,mid+1,r);
    	pushup(o);
    }
    
    inline void change(int o,int pos,int k){
    	int l=t[o].l,r=t[o].r;
    	if(pos==l && pos==r){
    		t[o].sum=k;
    		t[o].sum_2=1ll*k*k%mod;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(pos<=mid)
    		change(lson,pos,k);
    	else 
    		change(rson,pos,k);
    	pushup(o);
    }
    
    inline int query_sum(int o,int x,int y){
    	int l=t[o].l,r=t[o].r;
    	if(x<=l && r<=y){
    		return t[o].sum;
    	}
    	int mid=(l+r)>>1;
    	int res=0;
    	if(x<=mid)(res+=query_sum(lson,x,y))%=mod;
    	if(mid<y)(res+=query_sum(rson,x,y))%=mod;
    	return res%mod;
    }
    
    inline int query_sum_2(int o,int x,int y){
    	int l=t[o].l,r=t[o].r;
    	if(x<=l && r<=y){
    		return t[o].sum_2;
    	}
    	int mid=(l+r)>>1;
    	int res=0;
    	if(x<=mid)(res+=query_sum_2(lson,x,y))%=mod;
    	if(mid<y)(res+=query_sum_2(rson,x,y))%=mod;
    	return res%mod;
    }
    
    inline int ksm(int a,int k,int mod){
    	a%=mod,k%=mod;
    	int res=1;
    	for(;k;k>>=1){
    		if(k&1)res=1ll*res*a%mod;
    		a=1ll*a*a%mod;
    	} 
    	return res;
    }
    
    #undef int
    int main(){
    #define int long long
    //	freopen("a.in","r",stdin);
    	rd(n),rd(m);
    	rep(i,1,n)rd(val[i]);
    	build(1,1,n);
    	while(m--){
    		int op,x,y,k;
    		rd(op);
    		if(op==1){
    			rd(x),rd(k);
    			change(1,x,k);
    		}
    		else if(op==2){
    			rd(x),rd(y);
    			/*
    			方差=(a1^2+a2^2+...+an^2)/n  -  a_ba^2; 
    			*/
    			int len=ksm(y-x+1,mod-2,mod);
    			
    			int sum=query_sum(1,x,y)%mod;
    			int ba=sum*len%mod;
    			int ave=ba*ba%mod;
    			
    			int sum_2=query_sum_2(1,x,y)%mod;
    			sum_2=sum_2*len%mod; 
    			
    			int ans=sum_2-ave;
    			
    			while(ans<0)ans+=mod;
    			printf("%lld
    ",ans);
    		}
    	}
    	return 0;
    }
    

    B. 攀爬者

    ——模拟

    很水啊,只需按高度排个序计算距离就好了啊

    #include<bits/stdc++.h>
    using namespace std;
    
    template <typename T>inline void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;} 
    #define rep(i,a,b) for(int i=a;i<=b;++i)
    #define dwn(i,a,b) for(int i=a;i>=b;--i)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ee(i,u) for(int i=head[u];i;i=e[i].next)
    
    const int N=5e4+10;
    
    struct data{
    	int x,y,z;
    	bool operator < (const data &rhs)const{
    		return z<rhs.z;
    	}
    }p[N];
    
    double dist(int a,int b){
    	return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y)+(p[a].z-p[b].z)*(p[a].z-p[b].z));
    }
    
    int n;
    
    int main(){
    	rd(n);
    	rep(i,1,n){
    		rd(p[i].x),rd(p[i].y),rd(p[i].z);
    	}
    	sort(p+1,p+n+1);
    	double ans=0.000;
    	rep(i,1,n-1){
    		ans+=dist(i,i+1);
    	}
    	printf("%.3lf",ans);
    	return 0;
    }
    

    C. 蜈蚣

    ——区间放数学符号DP

    (f[i][j])表示考虑到第 i个位置,已经分了 j+1段时的最大异或值

    注意此处的 j+1段时因为k枚举的是能放几个" ^ "符号,因此最开始 m先要减 1

    然后区间DP,枚举上一个^号放在那里,(f[i][j]=max(f[i][j],f[k-1][j-1]+sum[i] xor sum[k-1]))

    int f[1010][110];
    int n,m;
    int sum[1010];
    
    int main(){
    	rd(n),rd(m);
    	--m;
    	rep(i,1,n){
    		int x;rd(x);
    		sum[i]=sum[i-1]^x;
    		f[i][0]=sum[i];
    	}
    	rep(i,1,n)
    		rep(j,1,min(i-1,m))
    			rep(k,1,i)
    				f[i][j]=max(f[i][j],f[k-1][j-1]+(sum[k-1]^sum[i]));
    	printf("%d
    ",f[n][m]);
    	return 0;
    }
    

    D. 漂浮的鸭子

    ——拓扑排序

    题意很明确:求图中的最大环

    使用拓扑排序求环

    由于拓扑排序每次都是从入度为0的点开始,而环上的点的入度都不会为0,所以环上的点就不会参加排序,也就是说,经过拓扑排序后剩下的边和点构成的都是环。

    这样我们就可以直接把每个环扫一遍记录最大环就结束了。

    const int N=1e5+10;
    
    struct Edge{
    	int v,w,next;
    }e[N];
    
    int head[N],edge_num;
    inline void adde(int u,int v,int w){
    	e[++edge_num]=(Edge){v,w,head[u]};
    	head[u]=edge_num;
    }
    
    int n,ans=0;
    int deg[N];
    
    queue<int>q;
    inline void topo_sort(){
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		ee(i,u){
    			int v=e[i].v;
    			if(--deg[v]==0)q.push(v);
    		} 
    	}
    }
    
    inline void get_ring(){
    	int res=0;
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		ee(i,u){
    			int v=e[i].v,w=e[i].w;
    			if(deg[v]==0)continue;
    			deg[v]--;
    			res+=w;
    			q.push(v);
    		}
    	}
    	ans=max(ans,res);
    }
    
    int main(){
    //	freopen("a.in","r",stdin);
    	rd(n);
    	rep(i,1,n){
    		int v,w;rd(v),rd(w);
    		adde(i,v,w);
    		deg[v]++;
    	}
    	rep(i,1,n)
    		if(deg[i]==0)
    			q.push(i);
    	topo_sort();
    	rep(i,1,n)
    		if(deg[i]){
    			q.push(i);
    			get_ring();
    		}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    E. 最大差值

    又是一道水题,30秒切掉

    只需要扫一遍就好了呀

    不多说了

    智娃水题,由于j>i,所以对于每一个j前的数,最小值是一定的!那么我们存一下minn,不断更新ans=max(ans,x-minn)就可以O(n)啦

    const int N=1e6+10;
    int n,minn=INT_MAX,ans;
    
    int main(){
    	rd(n);
    	rep(i,1,n){
    		int x;rd(x);
    		minn=min(minn,x);
    		ans=max(ans,x-minn); 
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    【没qiu搞懂】F. 随机数生成器

    ——数学

    int work(int x)
    {
        if(x==1)return 0;
        else return work(rand(1,x))+1;
    }
    

    ( ext{work()}) 返回值的期望是多少

    正解用到了欧拉常数 $gamma=0.5772156649015328… $(不会算啊只能百度一下了)

    有一个这样的公式

    (1+frac{1}{2}+frac{1}{3}+…+frac{1}{n}=ln n+gamma)

    可能跟这个有点关系吧……

    #include <cstdio>
    #include <cmath>
    using namespace std;
    double a=1,s=1;
    const double ola=0.5772156649015328;
    int n,i;
    int main()
    {
        scanf("%d",&n);
        if(n==1){printf("%.5f
    ",0);return 0;}
        if (n<=1000000)
        for(i=2;i<=n;i++)
        {
            a=(double)s/(i-1)+1;
            s+=a;
        }
        else
        a=log(n-1)+1+ola;
        printf("%.5f
    ",a);
        return 0;
    }
    

    G. 大循环

    ——秦九韶算法,组合数

    这个循环

    for(a[1]=1;a[1]<=n;a[1]++)
        for(a[2]=1;a[2]<=a[1];a[2]++)
            for(a[3]=1;a[3]<=a[2];a[3]++)
            …
            for(a[k]=1;a[k]<=a[k-1];a[k]++)
    

    刚开始看有些懵,之后发现原来很简单

    k层循环就相当于选了 k个数

    因此循环的次数就相当于从 n个数中选 k个数的方案总数,即 C(n,k)

    const int N=500010,mod=1e9+7;
    int n,m,k,q;
    int a[N],fac[N];
    
    inline int f(int x){
        int res=0;
        dwn(i,m,0)
            res=(res*x%mod+a[i])%mod;
        return res;
    }
    
    inline int ksm(int a,int k,int mod){
        k%=mod;
        int res=1;
        for(;k;k>>=1){
            if(k&1)res=1ll*res*a%mod;
            a=1ll*a*a%mod;
        }
        return res;
    }
    
    inline int inv(int x){return ksm(x,mod-2,mod);}
    
    inline int get_tot(){
        fac[0]=1;
        rep(i,1,n)
            fac[i]=fac[i-1]*i%mod;
        return fac[n]*inv(fac[n-k])%mod*inv(fac[k])%mod;
    }
    
    #undef int
    int main(){
    #define int long long
    	rd(n),rd(m),rd(k),rd(q);
    	q%=mod;//不加这句会WA两个点 
    	rep(i,0,m)rd(a[i]);
    	int tot=get_tot();
    	printf("%lld",1LL*f(q)*tot%mod);
    	return 0;
    }
    /*
    10 3 3 2
    1 3 3 1
    */
    //3240
    

    H. 会议座位

    ——map+归并排序求逆序对

    一对老师感到不满当且仅当他们的顺序和原顺序不同,如果原来是正序则现在是逆序,也就是说,原题意变为:求逆序对的个数

    const int N=1e5+10;
    int a[N],tmp[N];
    map<string,int>mp;
    string s;
    int n,ans=0;
    
    inline void merge_sort(int l,int r){
    	if(l>=r)return ;
    	int mid=(l+r)>>1;
    	merge_sort(l,mid);
    	merge_sort(mid+1,r);
    	int i=l,j=mid+1,k=l;
    	while(i<=mid && j<=r){
    		if(a[i]<=a[j])
    			tmp[k++]=a[i++];
    		else{
    			tmp[k++]=a[j++];
    			ans+=mid-i+1;
    		}
    	}
    	while(i<=mid)
    		tmp[k++]=a[i++];
    	while(j<=r)
    		tmp[k++]=a[j++];
    	rep(i,l,r)a[i]=tmp[i];
    }
    
    #undef int
    int main(){
    #define int long long
    	rd(n);
    	rep(i,1,n){
    		cin>>s;
    		mp[s]=i;
    	}
    	rep(i,1,n){
    		cin>>s;
    		a[i]=mp[s]; 
    	}
    	merge_sort(1,n);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    I. 生日礼物

    ——质数相关

    一道理解质数性质的好题

    一看见lcm(a,b)==n这么巧!!!
    对于n分解素数,(n=p1^{c1}+p2^{c2}+p3^{c3}+...+pn^{cn})那么对于每一个质数pi,都有(max(ci_a,ci_b)=ci;)

    开始讨论:当ci_a== ci,那么b有ci+1种选法(0也要算上),同理,当ci_b==ci,那么a有ci+1种选法

    加起来是2ci+2种,而当ci_a== ci_b==ci的这种情况被算了两边,再-1,得对于每一个素数是2*c[i]+1种,用乘法原理乘起来即可

    不要忘了n本身是一个大质数的情况

    #include<bits/stdc++.h>
    using namespace std;
    const int N=10000010;
    typedef long long ll;
    ll n,ans=1;
    int tot;
    bool flag[N];
    int prim[N];
    void prime()
    {
        for(int i=2;i<=N;i++)
        {
            if(!flag[i]) prim[++tot]=i;
            for(int j=1;j<=tot;j++)
            {
                if(i*prim[j]>N-1) break;
                flag[i*prim[j]]=1;
                if(i%prim[j]==0) break;
            }
        }
    }
    ll part(ll x)
    {
        for(int i=1;i<=tot;i++) 
        {
            int t=1;
            if(prim[i]*prim[i]>n) break;
            while(x%prim[i]==0) x/=prim[i],t++;
            if(t>1) ans*=(2*t-1);
        }
        if(x>1) ans*=(2*(1+1)-1);
        return ans;
    }
    int main()
    {
        prime();
        scanf("%lld",&n);
        printf("%lld",part(n));
        return 0;
    }
    

    J. HKE与他的小朋友们

    ——求环

    这个题和P2661信息传递有些类似,这个题同样是要求环

    对于每个点,我们按照指定的路线走,如果能到达起点,那么说明路径构成了一个环,环的大小就是一个周期,也就是说只要走环的大小的整倍数,就等价于没走。

    根据这一点,我们可以把所有的环都求出,然后用 换座次数 k 对环的大小取模,这样就可以求得每个小朋友换座后的位置。复杂度 O(nk)

    ( ext{p.s.})对于同一个环上的点,我们可以可以直接标记已经访问过,访问过的点就不需要再次找环了

    const int N=1e6+10;
    int n,k;
    int to[N],vis[N],ring[N],new_pos[N],ans[N];
    
    inline void get_ring(int st){
        int p=st,size=0;
        do{
            p=to[p];
            vis[p]=1;
            ring[++size]=p;
        }while(p!=st);
        int t=k%size;
        rep(i,1,size)
            new_pos[ring[i]]=ring[(i+t-1)%size+1];//防止数组下标为0
    }
    
    int main(){
        rd(n),rd(k);
        rep(i,1,n)rd(to[i]);
        rep(i,1,n)
            if(!vis[i])
                get_ring(i);
        rep(i,1,n)ans[new_pos[i]]=i;
        rep(i,1,n)
            printf("%d ",ans[i]);
        return 0;
    }
    

    【没qiu搞懂】K. 宝藏

    这个题我也不会额……直接上标程把,二维矩阵好像是一个二维树状数组

    #include<cstdio>
    const int maxn=2501;
    typedef int bit[maxn][maxn];
    int n,m,x1,y1,x2,y2,k,ch,a,b,tmp,ans,t;
    bit pre,ipre,jpre,ijpre;
    char ins[3];
    void read(int&x)
    {
        x=0,ch=getchar();
        while(ch>'9'||ch<'0')ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    }
    void modify(bit&s,int x,int y,int val)
    {
        for(register int i=x;i<=n;i+=i&-i)
            for(register int j=y;j<=n;j+=j&-j)
                s[i][j]^=val;
    }
    int query(bit&s,int x,int y)
    {
        int ans=0;
        for(register int i=x;i;i-=i&-i)for(register int j=y;j;j-=j&-j)ans^=s[i][j];
        return ans;
    }
    int Ask(int x2,int y2)
    {
        int t=0;
        if(x2*y2&1)t^=query(pre,x2,y2);
        if(x2&1)t^=query(jpre,x2,y2);
        if(y2&1)t^=query(ipre,x2,y2);
        return t^query(ijpre,x2,y2);
    }
    void Change(int i,int j,int val)
    {
        modify(pre,i,j,val);
        if((j-1)&1)modify(jpre,i,j,val);
        if((i-1)&1)modify(ipre,i,j,val);
        if((j-1)*(i-1)&1)modify(ijpre,i,j,val);
    }
    int Query(int x1,int y1,int x2,int y2){return Ask(x2,y2)^Ask(x1-1,y2)^Ask(x2,y1-1)^Ask(x1-1,y1-1);}
    void Modify(int x1,int y1,int x2,int y2,int val){Change(x1,y1,val),Change(x2+1,y1,val),Change(x1,y2+1,val),Change(x2+1,y2+1,val);}
    int main()
    {
        read(n),read(m);
        while(m--)
        {
            scanf("%s",ins);read(x1),read(y1),read(x2),read(y2);
            if(ins[0]=='Q')
            {
                ans=Query(x1,y1,x2,y2);
                for(register int i=0;i<30;++i)putchar(ans&(1<<i)?'2':'1');
                puts("");
            }
            else
            {
                read(k);t=0;
                while(k--)read(a),read(b),t^=((b&1)<<(a-1));
                if(t)Modify(x1,y1,x2,y2,t);
            }
        }
        return 0;
    }
    

    【没qiu搞懂】L. 简单的函数

    ——???

    考试的时候一直一直在找f(n)的规律,感觉应该是按照2,4,2,3,2,3,2,3,2,3,2,3的规律出现的,但是到后面出现了不一样的数……然后继续排错把破坏规律的位置排除掉后发现一些位置又有其他的不同破坏规律……总之最后只骗到50pt……

    正解不太懂啊……

    #include<cstdio>
    typedef long long ll;
    ll pre[55],m,cnt;
    const ll mod=1e9+7;
    ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
    ll qpow(ll a,ll n)
    {
        ll res=1;
        for(;n;(a*=a)%=mod,n>>=1)if(n&1)(res*=a)%=mod;
        return res;
    }
    int main()
    {
        pre[1]=1;
        for(register int i=2;i<=50;++i)pre[i]=lcm(pre[i-1],i);
        scanf("%lld",&m);
        for(register int i=3;i<=50;i+=2)cnt+=m/pre[i-1]-m/pre[i];
        printf("%lld
    ",qpow(2,(m-1)>>1)*qpow(3,cnt-1)%mod*qpow(4,(m>>1)-cnt)%mod);
        return 0;
    }
    

    M. 数列游戏

    ——区间dp

    我们要选择两个不互质的数进行消除,当且仅当两个数中间的所有的数都被消除

    f[i][j]f[i][j] 表示[i,j]区间的数被拿走数后能得到的最大值,因此我们最重要得到的答案是 f[1][n]f[1][n]

    最后注意开 ext{long long}long long !(不开爆零开后 ext{AC}AC )

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=810;
    int n;
    ll a[N],s[N],b[N],sum;
    ll f[N][N];
    ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&b[i]),s[i]=s[i-1]+b[i];
            if(i>1&&gcd(a[i-1],a[i])!=1) f[i-1][i]=b[i-1]+b[i];
        }
        for(int len=3;len<=n;len++)//区间长度
            for(int i=1;i<=n-len+1;i++)//左端点
            {
                int j=i+len-1;//右端点
                for(int k=i;k<j;k++) f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);//更新区间最大值
                if(f[i+1][j-1]==s[j-1]-s[i])//说明[i+1,j-1]已经全部被取走,这样j,i才能碰到
                    f[i][j]=max(f[i][j],f[i+1][j-1]+(gcd(a[i],a[j])==1?0:(b[i]+b[j])));
                else f[i][j]=max(f[i][j],f[i+1][j-1]);//否则直接转移子区间的值
            }
        printf("%lld",f[1][n]);
        return 0;
    }
    

    展示一种更优解法(跑的比标程还快):(from: ext{lwz dalao}lwz dalao )

    先预处理所有区间判断取走是否合法,再在按照线性dp转移

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=810;
    int n;
    ll a[N],b[N],s[N];
    ll f[N][2];//f[i][0/1]表示前i个数中i位置的数不删/删得到的最大值
    bool vis[N][N];//vis[i][j]=1表示[i,j]可以全部被取走
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) 
        {
            scanf("%lld",&a[i]);
            if(i>1&&gcd(a[i-1],a[i])!=1) vis[i-1][i]=1;
        }
        for(int i=1;i<=n;i++) scanf("%lld",&b[i]),s[i]=s[i-1]+b[i];
        for(int len=4;len<=n;len+=2)
            for(int i=1;i<=n-len+1;i++)
            {
                int j=i+len-1;
                if(vis[i+1][j-1]&&gcd(a[i],a[j])!=1) {vis[i][j]=1;continue;}
                for(int k=i+1;k<j-1;k++)
                    if(vis[i+1][k]&&vis[k+1][j-1]&&gcd(a[i],a[j])!=1)
                        {vis[i][j]=1;break;}
            }
        for(int i=2;i<=n;i++)
            for(int j=1;j<i;j++)
            {
                f[i][0]=max(f[i][0],max(f[j][0],f[j][1]));
                if(vis[j][i]) f[i][1]=max(f[i][1],f[j][0]+b[j]+b[i]+s[i-1]-s[j]);
            }
        printf("%lld",max(f[n][0],f[n][1]));
        return 0;
    }
    

    后记:这个是NOIp2018前最后一次大型ACM模拟赛了,感觉自己有些能想出来的部分还是有些欠缺,最后一天再巩固一下基础算法,争取在NOIp2018复赛取得佳绩!

    成败之机,在斯一举!

    P1471 方差(上道题的哥哥)

    还要维护一个区间加操作(自己再推一遍如何更新的)

    然鹅最坑的点是加的这个数k是一个实数阿伟调了好久好久!!!!

    主要是update(int o,int x,int y,int k)
    的int k要写成 double k啊我真是个智娃。

    以后看到题上写了实数俩字儿的时候请千万小心!

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>inline void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;} 
    #define rep(i,a,b) for(int i=a;i<=b;++i)
    #define dwn(i,a,b) for(int i=a;i>=b;--i)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ee(i,u) for(int i=head[u];i;i=e[i].next)
    
    #define lson o<<1
    #define rson o<<1|1
    const int N=100010;
    
    struct tree{
    	int l,r;
    	double sum,ssum,tag_add;
    }t[N<<2];
    
    double val[N];
    int n,m;
    
    inline void pushup(int o){
    	t[o].sum=(t[lson].sum+t[rson].sum);
    	t[o].ssum=(t[lson].ssum+t[rson].ssum);
    }
    
    inline void f(double delta,int o){
    	int l=t[o].l,r=t[o].r;
    	t[o].tag_add+=delta;
    	t[o].ssum=t[o].ssum+2*delta*t[o].sum+delta*delta*(r-l+1);
    	t[o].sum+=delta*(r-l+1);
    } 
    
    inline void pushdown(int o){
    	if(t[o].tag_add){
    		f(t[o].tag_add,lson);
    		f(t[o].tag_add,rson);
    		t[o].tag_add=0;
    	}
    }
    
    inline void build(int o,int l,int r){
    	t[o].tag_add=0;
    	t[o].l=l,t[o].r=r;
    	if(l==r){
    		t[o].sum=val[l];
    		t[o].ssum=val[l]*val[l];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(lson,l,mid);
    	build(rson,mid+1,r);
    	pushup(o);
    }
    
    inline void update(int o,int x,int y,double k){
    	int l=t[o].l,r=t[o].r;
    	if(x<=l && r<=y){
    		f(k,o);
    		return ;
    	}
    	pushdown(o);
    	int mid=(l+r)>>1;
    	if(x<=mid)update(lson,x,y,k);
    	if(mid<y)update(rson,x,y,k);
    	pushup(o);
    } 
    
    inline double query_sum(int o,int x,int y){
    	int l=t[o].l,r=t[o].r;
    	if(x<=l && r<=y){
    		return t[o].sum;
    	}
    	pushdown(o);
    	double res=0;
    	int mid=(l+r)>>1;
    	if(x<=mid)res=(res+query_sum(lson,x,y));
    	if(mid<y)res=(res+query_sum(rson,x,y));
    	return res;
    }
    
    inline double query_ssum(int o,int x,int y){
    	int l=t[o].l,r=t[o].r;
    	if(x<=l && r<=y){
    		return t[o].ssum;
    	}
    	pushdown(o);
    	double res=0;
    	int mid=(l+r)>>1;
    	if(x<=mid)res=(res+query_ssum(lson,x,y));
    	if(mid<y)res=(res+query_ssum(rson,x,y));
    	return res;
    }
    
    int main(){
    	#ifdef WIN32
    	freopen("fangcha.txt","r",stdin);
    	#endif
    	rd(n),rd(m);
    	rep(i,1,n)scanf("%lf",&val[i]);
    	build(1,1,n);
    	while(m--){
    		int op,x,y;
    		double k;
    		rd(op);
    		if(op==1){
    			rd(x),rd(y);scanf("%lf",&k);
    			update(1,x,y,k);
    		}
    		else if(op==2){
    			rd(x),rd(y);
    			double ans=query_sum(1,x,y)/(y-x+1);
    			printf("%.4lf
    ",ans); 
    		}
    		else if(op==3){
    			rd(x),rd(y);
    			double sum1=query_ssum(1,x,y)/(y-x+1);
    			double sum2=query_sum(1,x,y)/(y-x+1);
    			printf("%.4lf
    ",sum1-sum2*sum2);
    		}
    	}
    	return 0;
    }
    

    P2434 [SDOI2005]区间(合并区间模型)

    • 对于每个区间按照左端点从小到大排序
    • 枚举每一个区间;
      • 如果r<rang[i].l的话就输出当前区间(l,r),然后更新l=rang[i].l,r=rang[i].r;,继续去求下一个满足条件的区间。
      • else 说明两个区间有并,则更新r=max(r,range[i].r),其实左端点可以不用更新的(l=min(l,range[i].l),因为毕竟就是按照左端点来排得序嘛
    • 最后不要忘了输出l,r

    P2082 区间覆盖(加强版)上一题的弟弟

    在找到一个符合条件的区间的时候更新一下长度就好啦,没什么难的

    P5077 Tweetuzki 爱等差数列 推公式

    不开long long见祖宗

    手推一下柿子,倒序枚举,找到直接退出(这样可以保证找到的a1是最小的)。

  • 相关阅读:
    angular typescript 引入js文件
    (转)WEB页面导出为Word文档后分页&横向打印的方法
    aspx页面,后端通过Attributes.Add给textbox添加事件时,传参失效问题。
    aspx.designer.cs没有自动生成代码(没有自动注册)
    .net core 在CentOS环境下将微信公众号语音文件amr转化成mp3
    Sign in with Apple 后端验证(C#)
    C# 调用腾讯即时通信 IM
    LINQ入门笔记----LINQ To Object<Take(),TakeWhile(),Skip(),SkipWhile()>
    LINQ入门笔记----LINQ To Object<SelectMany()>
    初识LINQ
  • 原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11845572.html
Copyright © 2011-2022 走看看