SDOI2017Round1解题报告!

一分钟理解 AP(Affinity Propagation) 亲和⼒传播算法 这篇博客发出来后,我用 Rust 复现代码出现问题。为此,我对对照了 sklearn 的相关代码,反复比较了两天,发现一处 bug,把 += 误写成了 =,导致数据量大的时候完全无法聚类。这个debug过程让我仔细梳理的 AP 算法的计算过程,今天我对这篇博客做了大规模修改,以便能原样展现原论文思想。 阅读详情

Day1

T1 product

题目要求的式子是

∏i=1n∏j=1mf(gcd(i,j))

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

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

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

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

可以看出如果能够预处理所有T对应的g(T)=∏d|Tf(d)μ(Td),就可以O(n√logn)回答询问了。其中那个log是快速幂的。
但是ficonacci数列什么的显然不能线筛。那么只能考虑暴力一点来预处理了。
O(n√)枚举约数会T吧。。但是可以发现很多东西的μ值都是0,这些东西没有贡献,那么只考虑质因子不重复出现的因数就行了。
106范围内的数字质因子个数很少很少。可以二进制压位然后枚举。
线性筛一个最小质因子和去掉最小质因子以后的部分可以去掉分解质因数时的重复枚举,速度还是很快的。
然后就是μ值只有1和-1,那些-1对应的项是当做逆元乘进去的。这一部分不用每次都快速幂不然T死,可以用一个变量记一下,最后只需要做一个快速幂就可以了。
#include<cstdio>
#include<cstring>
#include<algorithm>
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<cstdio>
#include<cstring>
#include<algorithm>
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]]<size[u])
                son[v]=u;
              u=fa[u];
          }
    }
}
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]]<deep[top[y]]) swap(x,y);
        ans+=BIT::ask(w[x])-BIT::ask(w[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;i<n;i++){
        int 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<cstdio>
#include<cstring>
#include<algorithm>
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;i<p;i++) s[i][i]=1;
    }
    void get(){
        memset(s,0,sizeof(s));
        for (int i=0;i<p;i++)
          for (int j=0;j<p;j++){
              int v=(i+j)%p;
              s[i][v]+=c[j];
          }
    }
    Matrix operator * (const Matrix &a){
        Matrix c;
        for (int i=0;i<p;i++)
          for (int j=0;j<p;j++){
              long long *w=&(c.s[i][j]);
              for (int k=0;k<p;k++)
                (*w)+=s[i][k]*a.s[k][j]%Mod;
              (*w)%=Mod;
          }
        return 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;i<p;i++)
      c[i]=(m-i)/p+(i<=m);
    c[0]--;
    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]−mid∗B[i][j],如果最大权匹配的值大于0就可以扩大答案。因为边比较多所以费用流可能有点卡,但是好像faedbc学长的标算费用流只跑了0.5s?可怕= =好像把double乘以1e7强行搞成整数。。反正ATP这里写的是KM,跑起来还是非常快的。。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
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)<eps){
              vis[i+n]=mak;
              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<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
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;i<len;i++){
        int 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=LRxiyi−y¯∑i=LRxi−x¯∑i=LRyi+∑i=LRx¯y¯∑i=LRx2i−2∗x¯∑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<cstdio>
#include<cstring>
#include<algorithm>
#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;
}
MOS管的体二极管在LLC中的作用 MOSFET是一个三端器件(G、D、S),在N沟道MOSFET的结构中,源极和漏极之间的N型材料中自然形成了一个寄生的PN结,这个PN结就是体二极管(Body Diode),它是不可避免的器件结构。📌 对于N沟MOSFET:体二极管是从漏极 D 到源极 S 的正向导通二极管,方向为 D → S。MOS管的体二极管在LLC中并不是主功率通道,但却是实现软开关(ZVS)和能量过渡的关键“临时路径”。用得好,它是你的朋友;忽略它,电路就可能烧毁。 阅读详情

相关推荐

CiteSpace关键词聚类图谱含义详细解析

CiteSpace关键词聚类图谱含义详细解析 回顾上一次推文:CiteSpace关键词共现图谱含义详细解析 其中有一句: 当你人工已经可以很容易的进行归纳后,就不需要再利用CiteSpace聚类功能啦。 我们来看一下上次推文做出来的关键词共现图谱: 人工不好归纳!那怎么聚类呢? 此时便可使用CiteSpace的聚类功能啦! 如下所示:我们可以清晰地看到上边的关键词共现网络聚成了一个个不规则区域...

qq_39974284的博客 6万+

SDOI2017 总结

文章目录4948【SDOI2017】数字表格4949【SDOI2017】树点涂色4950【SDOI2017】序列计数4954【SDOI2017】新生舞会4956【SDOI2017】相关分析5417【SDOI2017】切树游戏 4948【SDOI2017】数字表格 莫比乌斯反演+数论分块 括号里面的部分O(n⋅lnn)O(n\cdot lnn)O(n⋅lnn)暴力维护(每个数更新自己倍数) 494...

qq_42835841的博客 509

Redis中什么是Big Key(大key)问题?如何解决Big Key问题?

通俗易懂的讲,Big Key就是某个key对应的value很大,占用的redis空间很大,本质上是大value问题。key往往是程序可以自行设置的,value往往不受程序控制,因此可能导致value很大。redis中这些Big Key对应的value值很大,在序列化/反序列化过程中花费的时间很大,因此当我们操作Big Key时,通常比较耗时,这就可能导致redis发生阻塞,从而降低redis性能。用几个实际的例子对大Key的特征进行描述:● 一个String类型的Key,它的值为5MB(数据过大); ● 一

weixiaohuai的博客 3万+

[SDOI2017]新生舞会

题目链接:[SDOI2017]新生舞会 显然 分数规划+费用流。 这道题比较卡精度,比较大小的时候不能加eps,直接比较,比较是否相等时需要eps。 AC代码: #include<bits/stdc++.h> //#define int long long using namespace std; const int N=110,M=1e6+10; const int inf=0x...

青烟绕指柔的博客 816

Aloudata 荣登“数智技术系列榜单”和“数据智能服务商 TOP30”

持续助力各行业数智化先进企业突破数据管理技术瓶颈,以更加敏捷、精准和可靠的数据交付驱动企业业务创新与经营提效

Aloudata的博客 667

R语言实战:GEO单细胞数据下载与自动化整理全流程(附代码)

本文详细介绍了使用R语言从GEO数据库下载scRNA-seq数据并实现自动化整理的完整流程。通过GEOquery和Seurat等工具包,读者可以高效获取单细胞RNA测序数据,完成从原始数据下载到预处理的全过程,最终生成可直接用于分析的Seurat对象。文章包含完整代码示例,特别适合生物信息学研究者处理单细胞数据。

weixin_29061821的博客 427

SDOI2017 题解

[SDOI2017]序列计数 发现至少有一个数是质数不是很好办,考虑用全部的减去没有一个质数的 令 fxf_xfx​表示当前模p余x的方案数,然后转移是一个卷积,因为p很小,上一个快速幂+暴力卷积就可以过了 然后没有一个质数的初值就是 fx%p=1(x∉P)f_{x\%p}=1(x\notin P)fx%p​=1(x∈/​P),总的个数的话全部设成1就可以了 [SDOI2017]树点涂色 发现将x...

FSYo 的博客 426

unity 腾讯地图sdk移动和缩放代码

unity 腾讯地图sdk 实现移动端 缩放 和 移动

。。。。。 1283

[SDOI2017]树点涂色

名可铭的题解报告

CM_20030210的博客 656

对称加密和非对称加密、公钥和私钥、单向认证和双向认证、数字签名、数字证书、根证书

文章目录1. 什么是公钥和私钥?2. 加密算法2.1 对称加密2.2 非对称加密2.2.1 非对称加密的重要性质2.2.2 非对称加密算法应用2.2.2.1 应用1 加密通信2.2.2.1.1 单向认证和双向认证2.2.2.1 应用2 数字签名3. 数字签名3.1 数字签名用法演示3.2 数字签名存在问题4. 数字证书(Digital Certificate)4.1 创建数字证书4.1.1 举例,CA如何给我们签发一个有效证书: 1. 什么是公钥和私钥? 摘自《什么是公钥和私钥?》 公钥(Public Ke

m0_45406092的博客 1万+

大模型部署指南:LM Studio

本文介绍使用LM Studio部署大模型

举世誉之而不加劝,举世非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。 1951

[SDOI2017]遗忘的集合,洛谷P3784,观察性质+莫比乌斯反演

正题 跟付公主的背包很像,可以先做做那一题. 然后也是ln,exp转化,ln求和之后发现不太好做,转化给f序列求ln,使等号两边相等. 问题就转化为了,选出一些数,使对于每一个i,在这些数里面的约数和为i*fi.显然是莫比乌斯反演即可. #include<bits/stdc++.h> using namespace std; int mod; int ksm(int x,int t){ int tot=1; while(t){ if(t&a...

Deep_Kevin的娱乐空间 279

Altium Designer应用技巧13: 四层板的拼版方法

本文介绍在Altium Designer中,进行4层板的拼版方法。Altium Designer有多种拼版方法,本文选择最直观的复制、删帖方法来完成,步骤如下: 1、看一下原始文件,如下图所示,我们要对该板进行拼版操作。 2、通过菜单“报告”-“板子信息”得到板子大小,在这里一般切换为mm比较好操作,我们可以使用快捷键"q"。 3、如果是水平拼版,我们需要关心水平长度;如果是垂直拼版...

snmplink的博客 1万+

SDOI2017 Round1 解题报告

Day1A product题意简述定义f(0)=0,f(1)=1,f(n)=f(n−1)+f(n−2)(n≥2)f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2)(n\ge 2) 求∏i=1n∏j=1mf(gcd(i,j))\prod\limits_{i=1}^n\prod\limits_{j=1}^m f(gcd(i,j)) 多组数据,对109+710^9+7取模数据范围对10%

zyf2000 1351

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

SDOI 2017 Round1 解题报告

Day 1 T1 数字表格 题目大意 · 求\(\prod\limits_{i=1}^n\prod\limits_{j=1}^mFibonacci(\gcd(i,j))\),\(T\leq1000\),\(n,m\leq10^6\) 思路 · 一言不合化式子(不失一般性地假设\(n<m\))\[\begin{aligned} ans&=\prod_{i=1}^n\prod...

as2886089的博客 169

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

SDOI2017 切树游戏

猫老师的基于变换合并的树上动态DP的链分治算法&SDOI2017切树游戏(cut)解题报告中的例题 一道神仙题. 参考了这位大佬的\(Blog\),大家快去%他 注意:此篇文章的约定: \(s_u\)表示节点\(u\)的子节点集合. \(ls_u\)表示节点\(u\)的轻儿子集合. 这个轻儿子指树剖之后的. 我们先列出简单\(dp\)方程. \(f[u][i]\)表示以\(u\)为最高...

weixin_30709929的博客 84

SDOI2017 Round1解题报告

虽然考的很差,很不想去再面对这套题,但是只有直面失败才能走向成功。从新审视这套题,才发现自己存在的问题和差距。Day 1T1题解mobius反演。。。 ∏ni=1∏mj=1fi[gcd(i,j)]\prod_{i=1}^n\prod_{j=1}^m fi[gcd(i,j)] ∏nk=1fi[k]∑ni=1∑mj=1[gcd(i,j)=k]\prod_{k=1}^n fi[k]^{\sum_{i=

clover_hxy的博客 1485

SDOI 2017 Round 1 滚粗记

zz选手的自白

Curious_Cat_is_OIer的博客 796

SDOI2017 Round1 Day1 题解

不知道有几个AK的,除了出题人SB搬了个BZOJ3779以外,应该没什么因素阻碍AK吧。要是SCOI考这套题多好。 BZOJ4816 数字表格 SB反演,推出答案为$\prod_{i=1}^nf^{\sum_{j=1}^{\left\lfloor\frac ni\right\rfloor}\mu(j)\left\lfloor\frac n{ij}\right\rfloor\left\lfloo...

weixin_30687811的博客 127

SDOI2017 Round2 滚粗记

其实SD二轮已经过去快一个星期了,一直不知道该怎么开始这次游记,所以拖到现在。。。DAY0从学校坐车去济南,发现跟一轮没什么区别,就是人少了一半。 和ATP坐在一起搞事情,zyf2000不舒服所以自己走了。 到颐正大厦后,跟lx住在一起,lx开开心心的出去找同学玩去了,自己叫了一份焗饭就下去领密码条。有了第一次挑密码条的教训,这次决定直接站着等,不一会密码条就到手了,结果发现和Yveh连座。╮(

clover_hxy的博客 1651

SDOI2017 Round1 续命记

Hope is a good thing. Maybe the best of things. And no good things ever dies.

zyf2000 1581

SDOI2017 Round1 题解

看这里 转载于:https://www.cnblogs.com/mleautomaton/p/11423148.html

araw94333的博客 222

【SDOI 2017】

1.今天看了一下SDOI2017 的题目,感觉很棒,很中肯的题目。接下来几天把这几个题写了。 (1)BZOJ 4816 。数字表格,看上去是个莫比乌斯反演的问题。待补 (2)BZOJ 4817 。树点涂色这个题目看上去应该是树链剖分+线段树的组合,但是没办法动态的维护树上信息,可能还得上LCT。 (3)BZOJ 4818。因为p很小,所以dp[i][j]表示前i个数,%p为j的方案数,转移是...

YG爱木木 240

SDOI2017 Round 1 游记

我也不知道为什么要参加省选 反正也AK不了,打打暴力玩。 day0 2点的火车,上午看了一早晨《winter journal》感觉要完。 于是弃疗了,火车上接着看,我发现我从没看过以第二人称写人生经历的 到了济南感觉好热啊,坐个公交车都快中暑了,然而去了山师还是站着,感觉要低血压了。 为什么好多人忘带报名费?? 我的饭钱都快被借完了 到达宾馆,感觉好高级啊,没住过这么强的宾馆 于是

yyx2000的博客 1630

[SDOI2017]序列计数 (矩阵加速,小容斥)

题面 Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数。 Alice还希望,这n个数中,至少有一个数是质数。 Alice想知道,有多少个序列满足她的要求。 输入格式 一行三个数,n,m,p。 对100\%的数据,1<=n <=1e9,1<=m <= 2e7,1<=p <=100 输出格式 一...

DD(XYX)的硫氰化氘博客 307

SDOI2017 遗忘的集合

题目描述: luogu bzoj 题解: 生成函数+多项式ln(+反演?) 首先如果我们已知$S$,那$S$中$i$的生成函数就是$1+x^i+x^{2i}+……$。 整理一下就是$ \frac{1} {1-x^i}$。 所以设$S$的生成函数为$F(x)$,则$F(x)= \prod _{i \in S} \frac{1}{1-x^i} $ 由于我们并不知道有哪些是答案...

a6t2007的博客 236

「SDOI2017」文本校正

题目 分类讨论六种排列: ABC:ABC:ABC:一次哈希判断。 CAB:CAB:CAB:枚举CCC的长度,总共nnn次哈希判断。 BCA:BCA:BCA:枚举AAA的长度,总共nnn次哈希判断。 CBA:CBA:CBA:构造新串P=s1tns2tn−1...snt1P = s_1t_ns_2t_{n-1}...s_nt_1P=s1​tn​s2​tn−1​...sn​t1​ 那么问题转化为将PPP划分为333个偶回文串。 枚举第一个回文串的位置[1....2x][1....2x][1....2x],那么就

Freopen的博客 700
上一篇: ATP的SDOI2017R1续命记
下一篇: [BZOJ2597][Wc2007]剪刀石头布(费用流)
FromATP
博客等级 码龄10年 75粉丝 163原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值