【2018.11.7】【luoguNOIp 热身赛】解题报告及总结
作者:互联网
reference:
暂告一段落,做的很爽!!
还剩5题,预计等国庆集训完再回来做
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
),因为毕竟就是按照左端点来排得序嘛
- 如果r<rang[i].l的话就输出当前区间(l,r),然后更新
- 最后不要忘了输出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_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本身是一个大质数的情况
标签:ci,2018.11,int,sum,long,luoguNOIp,区间,热身赛,define 来源: https://www.cnblogs.com/sjsjsj-minus-Si/p/11634657.html