【2018.11.7】【luoguNOIp 热身赛】解题报告及总结

reference:

暂告一段落,做的很爽!!
还剩5题,预计等国庆集训完再回来做

NOIp热身赛 比赛列表

P5142 区间方差

这题挺模板的,暴力单点修改,推一推方差的公式,发现只需要sum和ssum(区间平方和),然后逆元啥的……自己搞(为了练手,用了扩欧,当然ksm也是过得了的)

#include<bits/stdc++.h>
using namespace std;
#define int long long
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,mod=1e9+7;

struct tree{
	int l,r;
	int sum,ssum;
}t[N<<2];

int val[N];
int n,m;

inline void exgcd(int a,int b,int &x,int &y){
	if(!b)x=1,y=0;
	else exgcd(b,a%b,y,x),y-=a/b*x;
}

int inv(int a){
    int x,y;
    exgcd(a,mod,x,y);
    return (x+mod)%mod;
}

inline void pushup(int o){
	t[o].sum=(t[lson].sum+t[rson].sum)%mod;
	t[o].ssum=(t[lson].ssum+t[rson].ssum)%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]%mod;
		t[o].ssum=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].ssum=1LL*k*k%mod;
		t[o].sum=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 res=0;
	int mid=(l+r)>>1;
	if(x<=mid)res=(res+query_sum(lson,x,y))%mod;
	if(mid<y)res=(res+query_sum(rson,x,y))%mod;
	return res;
}

inline int 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;
	}
	int res=0;
	int mid=(l+r)>>1;
	if(x<=mid)res=(res+query_ssum(lson,x,y))%mod;
	if(mid<y)res=(res+query_ssum(rson,x,y))%mod;
	return res;
}

#undef int
int main(){
#define int long long
	#ifdef WIN32
	freopen("fangcha.txt","r",stdin);
	#endif 
	rd(n),rd(m);
	rep(i,1,n)rd(val[i]);
	build(1,1,n);
	while(m--){
		int op,x,k,y;
		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=inv(y-x+1);
			int sum=query_sum(1,x,y)%mod;
			int qsum=query_ssum(1,x,y)%mod;
			qsum=qsum*len%mod;
			int ba=sum*len%mod;
			int ave=ba*ba%mod;
			int ans=qsum-ave;
			while(ans<0)ans+=mod;//如果不加这句只能得10 pts!!! 
			printf("%lld\n",ans);
		}
	}
	return 0;
}

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\n",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\n",sum1-sum2*sum2);
		}
	}
	return 0;
}

P5146 最大差值[胸中的日月]

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

P1890 gcd区间 ST表

当你,有上线段树的冲动时,不妨看看ST表能不能搞。
显然,gcd是满足区间可“加”性的。而有没有什么修改操作……ST表无疑是一个酷酷的选择。(而且又好写又好调)

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是最小的)。

	rd(s); 
	int tot=sqrt(2*s)+1;
	dwn(i,tot,1){
		if((s*2+i-i*i)%(2*i)==0 && (s*2+i-i*i)/*分母>0,如果不写这句会WA两个点*/){
			printf("%lld %lld\n",(s*2+i-i*i)/(2*i),(s*2+i-i*i)/(2*i)+i-1);
			break;
		}
	}

P5144 蜈蚣 前缀异或和+dp

前缀异或和+dp

f[i][j]表示前i个位置放了j个^符号(分成了j+1段)

初值:f[i][0]=sum[i]

状态转移:
由区间[1,j-1][j,i]来更新f[i][k]

rep(i,1,n)
	rep(k,1,min(m,i-1))
		rep(j,k,i)
			f[i][k]=max(f[i][k],f[j-1][k-1]+(sum[i]^sum[j-1]));

P5149 会议座位 归并排序+map

很明显,在n<=1e5的情况下,逆序对最坏情况下是会达到100000*(100000-1)/2 == 4,999,950,000‬的!!这超出了int 的范围,要开long long.

不要每次等WA了再重新分析数据范围,要一遍就想明白。考场上不会有提交的feedback!

P5150 生日礼物 唯一分解定理

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

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

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

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

posted @ 2019-10-08 11:54  设计涉及社稷  阅读(147)  评论(0编辑  收藏  举报