SDOI2017Round1解题报告!

Day1

T1 product

题目要求的式子是

i=1nj=1mf(gcd(i,j))

其中 f(i) 表示fibonacci数列的第 i 项。
首先按照反演一贯的画柿子套路:
i=1nj=1md=1n[(i,j)=d]f(d)
d=1ni=1ndj=1md[(i,j)=1]f(d)

这个时候不能简单的只把 f(d) 提到前面了,因为可以看出来这实际上是一坨 f(d) 乘起来,有多少互质数对就乘了几次,所以那个互质数对个数的东西应该搞到指数上面去。
d=1nf(d)i=1ndj=1md[(i,j)=1]

可以直接把指数上那个东西用 μ 化一下,然后变成
d=1nf(d)i=1ndμ(i)nidmid

继续套路下去,设 id=T ,从枚举倍数转为枚举因数。
T=1n(d|Tf(d)μ(Td))nTmT

可以看出如果能够预处理所有 T 对应的 g(T)=d|Tf(d)μ(Td) ,就可以 O(nlogn) 回答询问了。其中那个log是快速幂的。
但是ficonacci数列什么的显然不能线筛。那么只能考虑暴力一点来预处理了。
O(n) 枚举约数会T吧。。但是可以发现很多东西的 μ 值都是0,这些东西没有贡献,那么只考虑质因子不重复出现的因数就行了。
106 范围内的数字质因子个数很少很少。可以二进制压位然后枚举。
线性筛一个最小质因子和去掉最小质因子以后的部分可以去掉分解质因数时的重复枚举,速度还是很快的。
然后就是 μ 值只有1和-1,那些-1对应的项是当做逆元乘进去的。这一部分不用每次都快速幂不然T死,可以用一个变量记一下,最后只需要做一个快速幂就可以了。

#include
#include
#include
using namespace std;
const int N=1000000;
const long long Mod=1000000007;
const long long phi=Mod-1;
int T,n,m,prm[1000010],e[1000010],g[1000010],v[20],d[2010],mu[1000010],f[1000010];
long long F[1000010],ans;
bool ext[1000010];
void get_prime(int N){
    mu[1]=e[1]=g[1]=1;
    for (int i=2;i<=N;i++){
        if (ext[i]==false){
            prm[++prm[0]]=i;
            e[i]=i;g[i]=1;mu[i]=-1;
        }
        for (int j=1;j<=prm[0];j++){
            if ((long long)i*prm[j]>N) break;
            ext[i*prm[j]]=true;
            if (i%prm[j]==0){
                e[i*prm[j]]=e[i];
                g[i*prm[j]]=g[i];
                break;
            }else{
                e[i*prm[j]]=prm[j];
                g[i*prm[j]]=i;
                mu[i*prm[j]]=-mu[i];
            }
        }
    }
}
long long powww(long long a,int t){
    long long ans=1;
    a%=Mod;t=(t+phi)%phi;
    while (t!=0){
        if (t&1) ans=(ans*a)%Mod;
        a=(a*a)%Mod;t>>=1;
    }
    return ans;
}
long long get_F(int T){
    long long sum=f[T],inv=1,r=T;
    v[0]=0;d[0]=1;
    while (r!=1){v[++v[0]]=e[r];r=g[r];}
    for (int i=1;i<=v[0];i++)
      d[1<<(i-1)]=v[i];
    for (int i=1;i<(1<<(v[0]));i++){
        int b=i&(-i);
        d[i]=d[i^b]*d[b];
        if (mu[d[i]]==-1) inv=inv*f[T/d[i]]%Mod;
        else sum=sum*f[T/d[i]]%Mod;
    }
    sum=sum*powww(inv,Mod-2)%Mod;
    return sum;
}
int main()
{
    freopen("product.in","r",stdin);
    freopen("product.out","w",stdout);
    scanf("%d",&T);
    get_prime(N);
    f[0]=0;f[1]=1;F[0]=1;
    for (int i=2;i<=N;i++)
      f[i]=(f[i-1]+f[i-2])%Mod;
    for (int i=1;i<=N;i++) F[i]=get_F(i);
    for (int i=1;i<=N;i++) F[i]=F[i]*F[i-1]%Mod;
    for (int wer=1;wer<=T;wer++){
        scanf("%d%d",&n,&m);
        ans=1;if (n>m) swap(n,m);
        for (int i=1,tail;i<=n;i=tail+1){
            tail=min(m/(m/i),n/(n/i));
            long long t,tmp;
            t=(long long)(n/i)*(long long)(m/i)%phi;
            tmp=F[tail]*powww(F[i-1],Mod-2)%Mod;
            ans=ans*powww(tmp,t)%Mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

T2 paint

这题就是学姐出过原题的那个题。。
重要的提示是每次只修改到根节点的路径,并且每次染的颜色是不一样的。
那么每次如果染了一条链的话就会造成沿路的很多链跟原来一样颜色的节点断开。
并且我们不关心具体是什么颜色,我们只关心有多少种颜色。
那么原来就是一样颜色的节点不需要被修改。
这个操作和LCT中的Access操作是一样的。
初始所有节点都只有单向指到父亲位置的指针,因为颜色都不一样。LCT中的轻重边实际上在这个题当中代表的是它走到它父亲需不需要换颜色。那么Access的时候断开的边就相当于从不换颜色变成了换颜色,这条边首先要标记便于处理2操作,然后子树内所有节点对应3操作的答案要+1。如果Access的时候连上了这条边就要去掉这条边的标记,然后子树-1。
那么2操作就是一个树链上标记数量查询问题,就是简单的单点修改区间查询啦。
考场的时候为了搞常数把这一部分改成了树状数组。。
3操作就是维护线段树区间加减标记然后求区间最大值了。
注意Access的时候要修改的节点不是Splay的根节点而是这条树链上最靠上的节点,也就是Splay上最左边的那个节点。
注意的问题是最后答案要+1。

#include
#include
#include
using namespace std;
int n,m,p[100010],a[200010],nxt[200010],deep[100010],w[100010],out[100010];
int cur[100010],top[100010],size[100010],son[100010],cnt,tot,fa[100010];
int Max[400010],dlt[400010],num[100010];
struct Node{
    Node *ch[2],*fa;
    int id;
    Node();
    Node(int i);
    Node* findrt();
    bool pl(){return this==fa->ch[1];}
    bool is_root(){return this!=fa->ch[0]&&this!=fa->ch[1];}
}*null,T[100010],*ptr[100010];
Node::Node(){ch[1]=ch[0]=fa=null;id=0;}
Node::Node(int i){ch[1]=ch[0]=fa=null;id=i;}
Node* Node::findrt(){
    Node *k=this;
    while (k->ch[0]!=null) k=k->ch[0];
    return k;
}
void add(int x,int y){
    tot++;a[tot]=y;nxt[tot]=p[x];p[x]=tot;
}
void Rotate(Node *k){
    Node *r=k->fa;
    if (r==null||k==null) return;
    int x=k->pl()^1;
    r->ch[x^1]=k->ch[x];
    if (k->ch[x]!=null)
      r->ch[x^1]->fa=r;
    if (!r->is_root())
      r->fa->ch[r->pl()]=k;
    k->fa=r->fa;
    r->fa=k;
    k->ch[x]=r;
}
void Splay(Node *r){
    for (;!r->is_root();Rotate(r))
      if(!r->fa->is_root())
        Rotate(r->pl()==r->fa->pl()?r->fa:r);
}
void dfs(int rt){
    int u=rt;
    bool flag;
    while (true){
        if (deep[u]==0){
            deep[u]=deep[fa[u]]+1;
            cur[u]=p[u];size[u]=1;son[u]=0;
        }
        flag=false;
        for (int i=cur[u];i!=0;i=nxt[i]){
            cur[u]=nxt[i];
            if (a[i]!=fa[u]){
                fa[a[i]]=u;u=a[i];flag=true;break;
            }
        }
        if (flag==false)
          if (u==rt) break;
          else{
              int v=fa[u];
              size[v]+=size[u];
              if (size[son[v]]void dfs_again(int rt){
    int u=rt,tp=rt;
    bool flag;
    while (true){
        if (w[u]==0){
            w[u]=++cnt;top[u]=tp;
            num[cnt]=u;cur[u]=p[u];
            if (son[u]!=0){u=son[u];continue;}
        }
        flag=false;
        for (int i=cur[u];i!=0;i=nxt[i]){
            cur[u]=nxt[i];
            if (a[i]!=fa[u]&&a[i]!=son[u]){
                tp=u=a[i];flag=true;break;
            }
        }
        if (flag==false){
            out[u]=cnt;
            if (u==rt) break;
            else u=fa[u];
        }
    }
}
namespace BIT{
    int s[100010];
    int lowbit(int i){return i&(-i);}
    void add(int i,int v){
        while (i<=n){s[i]+=v;i+=lowbit(i);}
    }
    int ask(int i){
        int ans=0;
        while (i!=0){ans+=s[i];i-=lowbit(i);}
        return ans;
    }
}
void update(int i){Max[i]=max(Max[i<<1],Max[(i<<1)+1]);}
void build(int i,int l,int r){
    if (l==r){Max[i]=deep[num[l]]-1;return;}
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)+1,mid+1,r);
    update(i);
}
void pushdown(int i){
    if (dlt[i]!=0){
        dlt[i<<1]+=dlt[i];
        dlt[(i<<1)+1]+=dlt[i];
        Max[i<<1]+=dlt[i];
        Max[(i<<1)+1]+=dlt[i];
        dlt[i]=0;
    }
}
int ask(int i,int l,int r,int left,int right){
    if (left<=l&&right>=r) return Max[i];
    int mid=(l+r)>>1,ans=0;
    pushdown(i);
    if (left<=mid) ans=max(ans,ask(i<<1,l,mid,left,right));
    if (right>mid) ans=max(ans,ask((i<<1)+1,mid+1,r,left,right));
    return ans;
}
void addnum(int i,int l,int r,int left,int right,int val){
    if (left<=l&&right>=r){dlt[i]+=val;Max[i]+=val;return;}
    int mid=(l+r)>>1;
    pushdown(i);
    if (left<=mid) addnum(i<<1,l,mid,left,right,val);
    if (right>mid) addnum((i<<1)+1,mid+1,r,left,right,val);
    update(i);
}
void Change(int x,int v){//把x的父边修改成v这种颜色 
    int now=BIT::ask(w[x])-BIT::ask(w[x]-1);
    if (now==v) return;
    now=(now==1)?-1:1;
    BIT::add(w[x],now);
    if (v==0) now=-1;
    else now=1;
    addnum(1,1,n,w[x],out[x],now);
}
void Access(Node *k){
    Node *r=null,*now;
    while (k!=null){
        Splay(k);
        if (k->ch[1]!=null){
            now=k->ch[1]->findrt();
            Change(now->id,1);
        }
        k->ch[1]=r;
        if (r!=null){
            now=r->findrt();
            Change(now->id,0);
        }
        r=k;k=k->fa;
    }
}
int Query(int x,int y){
    int ans=0;
    while (top[x]!=top[y]){
        if (deep[top[x]]1);
        x=fa[top[x]];
    }
    if (x==y) return ans;
    if (deep[x]>deep[y]) swap(x,y);
    ans+=BIT::ask(w[y])-BIT::ask(w[x]);
    return ans; 
}
int main()
{
    freopen("paint.in","r",stdin);
    freopen("paint.out","w",stdout);
    null=new Node;*null=Node();
    scanf("%d%d",&n,&m);
    for (int i=1;iint x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for (int i=1;i<=n;i++){T[i]=Node(i);ptr[i]=T+i;}
    dfs(1);dfs_again(1);
    build(1,1,n);
    for (int i=2;i<=n;i++) BIT::add(i,1);
    for (int i=1;i<=n;i++)
      if (fa[i]!=0) ptr[i]->fa=ptr[fa[i]];
    for (int i=1;i<=m;i++){
        int type,x,y;
        scanf("%d",&type);
        if (type==1){
            scanf("%d",&x);
            Access(ptr[x]);
        }
        if (type==2){
            scanf("%d%d",&x,&y);
            printf("%d\n",Query(x,y)+1);    
        }
        if (type==3){
            scanf("%d",&x);
            printf("%d\n",ask(1,1,n,w[x],out[x])+1);
        }
    }
    return 0;
}

T3 count

首先考虑暴力DP,用 f[i][j] 表示选了i个数字,当前模p的余数是j的方案数。转移的时候枚举所有可以用的数字或者干脆用一个数组统计余数是k的数字数量就可以转移了。对于至少有一个质数的限制,用总的减去一个质数也没有的。
这个DP是可以矩阵乘法优化的。矩阵就是用那个统计余数是k的数字数量的数组来构造就可以了。对于每个已经有的余数 i 和可以添加到后面的余数 j ,矩阵的 s[i][(i+j)%p] 要加上 cnt[j]
最后要求的 f[n][0] 实际上就是矩阵 s[0][0] 位置的数字。

#include
#include
#include
using namespace std;
const long long Mod=20170408;
int n,m,p,c[1010],prm[20000010];
bool ext[20000010];
long long ans;
struct Matrix{
    long long s[110][110];
    Matrix(){memset(s,0,sizeof(s));}
    void clear(){
        memset(s,0,sizeof(s));
        for (int i=0;i1;
    }
    void get(){
        memset(s,0,sizeof(s));
        for (int i=0;ifor (int j=0;jint v=(i+j)%p;
              s[i][v]+=c[j];
          }
    }
    Matrix operator * (const Matrix &a){
        Matrix c;
        for (int i=0;ifor (int j=0;jlong long *w=&(c.s[i][j]);
              for (int k=0;kreturn c;
    }
}w;
void get_prime(int N){
    ext[1]=true;
    for (int i=2;i<=N;i++){
        if (ext[i]==false){
            prm[++prm[0]]=i;
            c[i%p]--;
        }
        for (int j=1;j<=prm[0];j++){
            if ((long long)i*prm[j]>N) break;
            ext[i*prm[j]]=true;
            if (i%prm[j]==0) break;
        }
    }
}
Matrix powww(Matrix a,int t){
    Matrix ans;
    ans.clear();
    while (t!=0){
        if (t&1) ans=ans*a;
        a=a*a;t>>=1;
    }
    return ans;
}
int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    scanf("%d%d%d",&n,&m,&p);
    for (int i=0;i0]--;
    w.get();w=powww(w,n);
    ans=w.s[0][0];
    get_prime(m);
    w.get();w=powww(w,n);
    ans-=w.s[0][0];
    ans=(ans+Mod)%Mod;
    printf("%I64d\n",ans);
    return 0;
}

Day2

T1 ball

比较显然的01分数规划。二分一个答案 mid ,然后男生女生两排点建立二分图,中间的边权连的是 A[i][j]midB[i][j] ,如果最大权匹配的值大于0就可以扩大答案。因为边比较多所以费用流可能有点卡,但是好像faedbc学长的标算费用流只跑了0.5s?可怕= =好像把double乘以1e7强行搞成整数。。反正ATP这里写的是KM,跑起来还是非常快的。。

#include
#include
#include
#include
using namespace std;
const double eps=1e-10;
const double inf=1e12;
int n,A[110][110],B[110][110],vis[210],mak,link[110],sum;
double e[110][110],x[110],y[110],lak[110];
bool find(int u,int mak){
    vis[u]=mak;
    for (int i=1;i<=n;i++)
      if (vis[i+n]!=mak){
          double tmp=x[u]+y[i]-e[u][i];
          if (fabs(tmp)if (link[i]==-1||find(link[i],mak)){
                  link[i]=u;return true;
              }
          }else lak[i]=min(lak[i],tmp);
      }
    return false;
}
double KM(){
    double sum=0;
    for (int i=1;i<=n;i++){
        x[i]=-inf;
        for (int j=1;j<=n;j++)
          x[i]=max(x[i],e[i][j]);
    }
    memset(y,0,sizeof(y));
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++) lak[j]=inf;
        while (true){
            ++mak;
            if (find(i,mak)) break;
            double tmp=inf;
            for (int k=1;k<=n;k++)
              if (vis[k+n]!=mak)
                tmp=min(tmp,lak[k]);
            for (int k=1;k<=n;k++)
              if (vis[k]==mak) x[k]-=tmp;
            for (int k=1;k<=n;k++)
              if (vis[k+n]==mak) y[k]+=tmp;
              else lak[k]-=tmp;
        }
    }
    for (int i=1;i<=n;i++)
      if (link[i]!=-1)
        sum+=e[link[i]][i];
    return sum;
}
double check(double lim){
    memset(vis,0,sizeof(vis));
    mak=0;memset(link,-1,sizeof(link));
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
        e[i][j]=(double)A[i][j]-lim*(double)B[i][j];
    return KM();
}
double Divide(double l,double r){
    double mid;
    while (r-l>eps){
        mid=(l+r)/2;
        if (check(mid)>-eps) l=mid;
        else r=mid;
    }
    return (l+r)/2;
}
int main()
{
    freopen("ball.in","r",stdin);
    freopen("ball.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++){
          scanf("%d",&A[i][j]);
          sum+=A[i][j];
      }
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
        scanf("%d",&B[i][j]);
    printf("%.6lf\n",Divide(0,sum));
    return 0;
}

T2 game

这题ATP考场上一点思路没有。。
关键的思路就是把所有没有到达终止点的状态认为是相同的,设为 N
如果A同学猜的串是TTH,B同学猜的串是HTT,那么N后面接上一个TTH一定能到达终止状态,因为最坏情况下也会在A那边终止。
但是有可能到不了最后一个T就结束了,因为可能到达了B的结束状态。比如如果N的结尾是HT,那么只来一个T就结束了。再如果N的结尾是H,那么来两个T也结束了。
那么实际上就有一个方程: P(NTTH)=P(A)+P(BTH)+P(BH)
化成数字就是 0.125N=A+0.75B
可以发现如果A的后缀是B的前缀,那么A就会在B的方程里有一个系数。
每个人的串都有一个方程,再加上所有同学胜利的概率总和为1,最后一共n+1个未知量和n+1个方程,就可以高斯消元了。
方程的系数可以建立AC自动机,记录自动机上每个节点关联的串的编号,然后对于每个串的结尾节点顺着fail指针往上跳就可以了。

#include
#include
#include
#include
using namespace std;
const double eps=1e-8;
int n,m,ch[100010][2],fail[100010],p[100010],a[100010],nxt[100010],rec[310],tot,cnt,head,tail,q[100010],step[100010];
bool end[100010];
double A[510][510],B[510],ans[310],bin[310];
char S[310];
void add(int x,int y){
    tot++;a[tot]=y;nxt[tot]=p[x];p[x]=tot;
}
void insert(char *s,int len,int id){
    int now=0;
    for (int i=0;iint x=(s[i]=='T')?0:1;
        if (ch[now][x]==0){
            ch[now][x]=++cnt;
            step[cnt]=step[now]+1;
        }
        now=ch[now][x];
        add(now,id);
    }
    end[now]=true;rec[id]=now;
}
void get_fail(){
    head=tail=0;
    for (int i=0;i<=1;i++)
      if (ch[0][i]!=0) q[++tail]=ch[0][i];
    while (head!=tail){
        int u=q[++head];
        for (int i=0;i<=1;i++)
          if (ch[u][i]!=0){
              fail[ch[u][i]]=ch[fail[u]][i];
              q[++tail]=ch[u][i];
          }else ch[u][i]=ch[fail[u]][i];
    }
}
void Gauss_Eli(int N){
    int num;
    for (int i=1;i<=N;i++){
        num=i;
        for (int j=i;j<=N;j++)
          if (fabs(A[j][i])>eps){
              num=j;break;
          }
        for (int j=1;j<=N;j++) swap(A[i][j],A[num][j]);
        swap(B[i],B[num]);
        for (int j=i+1;j<=N;j++)
          if (fabs(A[j][i])>eps){
              double t=A[j][i]/A[i][i];
              for (int k=1;k<=N;k++)
                A[j][k]-=A[i][k]*t;
              B[j]-=B[i]*t;
          }
    }
    for (int i=N;i>=1;i--){
        ans[i]=B[i]/A[i][i];
        for (int j=i-1;j>=1;j--)
          B[j]-=ans[i]*A[j][i];
    }
}
void Jump(int now,int id){
    A[id][n+1]=-bin[m];
    while (now!=0){
        for (int i=p[now];i!=0;i=nxt[i])
          A[a[i]][id]+=bin[m-step[now]];
        now=fail[now];
    }
}
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        scanf("%s",S);
        insert(S,m,i);
    }
    bin[0]=1;
    for (int i=1;i<=m;i++) bin[i]=bin[i-1]*0.5;
    get_fail();
    for (int i=1;i<=n;i++) Jump(rec[i],i);
    for (int i=1;i<=n;i++) A[n+1][i]=1;
    B[n+1]=1;
    Gauss_Eli(n+1);
    for (int i=1;i<=n;i++) printf("%.10lf\n",ans[i]);
    return 0;
}

T3 relative

首先把要求的式子化开:

i=LRxiyiy¯i=LRxix¯i=LRyi+i=LRx¯y¯i=LRx2i2x¯i=LRxi+i=LRx¯2

然后我们可以发现我们需要维护x和y的区间和,x和y的乘积和还有x的平方和。平均数可以通过区间和还有区间长度算出来。

那么对于这几个东西如何在修改的时候维护呢?
对于增加一个常量的操作,区间和比较简单,乘积和的话可以发现每个位置的数字从 i=LRxiyi 变成 i=LR(xi+S)(yi+T) ,展开就可以变成 i=LRxiyi+i=LRSyi+i=LRTxi+i=LRST ,那么用原来的区间和进行计算就可以了。
平方和的话是类似的展开方式,化成 i=LRx2i+i=LR2Sxi+i=LRS2
对于覆盖操作,区间和会变成 i=LRS+i=LRi ,那么只要知道这一段区间内下标的和就可以了。可以用等差数列求和公式也可以直接在线段树里维护出来。
然后乘积和会变成 i=LRST+i=LRSi+i=LRTi+i=LRi2 。平方和是类似的展开方式。
总而言之就是用维护的那些量互相算一算。
对于加法标记和覆盖标记冲突的问题像普通线段树加法和覆盖标记一样处理就可以了。

#include
#include
#include
#define inf 1000000000
using namespace std;
const double eps=1e-8;
int n,m,X[100010],Y[100010];
double Xs[400010],Ys[400010],Sq[400010],Mul[400010],Xdlt[400010],Ydlt[400010];
double Is[400010],Is2[400010],Xcov[400010],Ycov[400010];
struct record{
    double sx,sy,sq,ml;
    record(){sx=sy=sq=ml=0;}
};
void update(int i){
    Xs[i]=Xs[i<<1]+Xs[(i<<1)+1];
    Ys[i]=Ys[i<<1]+Ys[(i<<1)+1];
    Sq[i]=Sq[i<<1]+Sq[(i<<1)+1];
    Mul[i]=Mul[i<<1]+Mul[(i<<1)+1];
}
void Cov(int i,int l,int r,double cx,double cy){
    double len=(r-l+1);
    Xdlt[i]=Ydlt[i]=0;
    Xcov[i]=cx;Ycov[i]=cy;
    Sq[i]=cx*cx*len+2*cx*Is[i]+Is2[i];
    Mul[i]=cx*cy*len+(cx+cy)*Is[i]+Is2[i];
    Xs[i]=cx*len+Is[i];Ys[i]=cy*len+Is[i];
}
void Add(int i,int l,int r,double dx,double dy){
    double len=r-l+1;
    if (Xcov[i]!=inf||Ycov[i]!=inf){
        Cov(i,l,r,Xcov[i]+dx,Ycov[i]+dy);
        return;
    }
    Xdlt[i]+=dx;Ydlt[i]+=dy;
    Sq[i]+=dx*dx*len+2*dx*Xs[i];
    Mul[i]+=dx*Ys[i]+dy*Xs[i]+dx*dy*len;
    Xs[i]+=dx*len;Ys[i]+=dy*len;
}
void pushdown(int i,int l,int r){
    if (Xdlt[i]!=0||Ydlt[i]!=0){
        int mid=(l+r)>>1;
        Add(i<<1,l,mid,Xdlt[i],Ydlt[i]);
        Add((i<<1)+1,mid+1,r,Xdlt[i],Ydlt[i]);
        Xdlt[i]=Ydlt[i]=0;
    }
    if (Xcov[i]!=inf||Ycov[i]!=inf){
        int mid=(l+r)>>1;
        Cov(i<<1,l,mid,Xcov[i],Ycov[i]);
        Cov((i<<1)+1,mid+1,r,Xcov[i],Ycov[i]);
        Xcov[i]=Ycov[i]=inf;
    }
}
void build(int i,int l,int r){
    Xcov[i]=Ycov[i]=inf;
    if (l==r){
        double xx=X[l],yy=Y[l];
        Xs[i]=xx;Ys[i]=yy;
        Sq[i]=xx*xx;Mul[i]=xx*yy;
        Is[i]=l;Is2[i]=(double)l*(double)l;
        return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)+1,mid+1,r);
    update(i);
    Is[i]=Is[i<<1]+Is[(i<<1)+1];
    Is2[i]=Is2[i<<1]+Is2[(i<<1)+1];
}
void ask(int i,int l,int r,int left,int right,record &ans){
    if (left<=l&&right>=r){
        ans.sx+=Xs[i];ans.sy+=Ys[i];
        ans.sq+=Sq[i];ans.ml+=Mul[i];
        return;
    }
    int mid=(l+r)>>1;
    pushdown(i,l,r);
    if (left<=mid) ask(i<<1,l,mid,left,right,ans);
    if (right>mid) ask((i<<1)+1,mid+1,r,left,right,ans);
}
double Getans(int l,int r){
    record tmp;
    double avex,avey,len=r-l+1,up,down;
    up=down=0;ask(1,1,n,l,r,tmp);
    avex=tmp.sx/len;avey=tmp.sy/len;
    down=tmp.sq-2*avex*tmp.sx+avex*avex*len;
    up=tmp.ml-avey*tmp.sx-avex*tmp.sy+avex*avey*len;
    return up/down+eps;
}
void add(int i,int l,int r,int left,int right,double s,double t){
    if (left<=l&&right>=r){Add(i,l,r,s,t);return;}
    int mid=(l+r)>>1;
    pushdown(i,l,r);
    if (left<=mid) add(i<<1,l,mid,left,right,s,t);
    if (right>mid) add((i<<1)+1,mid+1,r,left,right,s,t);
    update(i);
}
void cover(int i,int l,int r,int left,int right,double s,double t){
    if (left<=l&&right>=r){Cov(i,l,r,s,t);return;}
    int mid=(l+r)>>1;
    pushdown(i,l,r);
    if (left<=mid) cover(i<<1,l,mid,left,right,s,t);
    if (right>mid) cover((i<<1)+1,mid+1,r,left,right,s,t);
    update(i);
}
int main()
{
    freopen("relative.in","r",stdin);
    freopen("relative.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&X[i]);
    for (int i=1;i<=n;i++) scanf("%d",&Y[i]);
    build(1,1,n);
    for (int i=1;i<=m;i++){
        int type,l,r,s,t;
        scanf("%d%d%d",&type,&l,&r);
        if (type==1) printf("%.10lf\n",Getans(l,r));
        if (type==2){
            scanf("%d%d",&s,&t);
            add(1,1,n,l,r,s,t);
        }
        if (type==3){
            scanf("%d%d",&s,&t);
            cover(1,1,n,l,r,s,t);
        }
    }
    return 0;
}

你可能感兴趣的:(我也不知道该算什么分类)