6.26--集查并--鲜花--fib

fib:
题意是区间加斐波那契数,区间求$\sum\limits_{i=l}^{r}{a_{i}^{2}}$

题外话:比赛时用的是广义斐波那契数的等式$s_{i}=x*f_{i-2}+y*f_{i-1}(x是第一项,y是第二项)$.但是这个东西在线段树上没办法维护,早上没看出来,寄飞了.

对于区间加斐波那契数,区间求和这个子问题是CF446C,本题做法采用了类似第一篇题解的维护方式.
首先要知道斐波那契数的性质:
$$f_{n+m}=f_{n+1}f_{m}+f_{n}f_{m-1}$$
这个恒等式是用来维护区间的.
利用上式区间$[l,r]$修改其实就是$(s_{i}+f_{i-l+1})^2=(s_{i}+f_{i}f_{-l}+f_{i+1}f_{1-l})^2$.
因为$f_{i}$和$f_{i+1}$是常量,所以是可以合并的.
所以ans:$$=\sum\limits_{i=l}^{r}{(f_{i}*x+f_{i+1}*y)^2}$$$$=\sum\limits_{i=l}^{r}{(f_{i}*x)^2}+\sum\limits_{i=l}^{r}{(f_{i+1}*y)^2}+\sum\limits_{i=l}^{r}{2*f_{i}*f_{i+1}*x*y}$$
$sum1:\sum\limits_{i=l}^{r}{(f_{i}*x)^2}$  $sum2:\sum\limits_{i=l}^{r}{(f_{i+1}*y)^2}$  $sum3:\sum\limits_{i=l}^{r}{2*f_{i}*f_{i+1}*x*y}$
我们每次给$x$增加$f_{-l}$,给$y$增加$f_{1-l}$
其中$$\sum\limits_{i=l}^{r}{(f_{i}*(x+val))^2}$$$$=\sum\limits_{i=l}^{r}{(f_{i}*x)^2+(val+f_{i})^2+(2*val*x*f_{i}^2)}$$

$y$的维护是同理的.
$sum4:\sum\limits_{i=l}^{r}{(2*val*x*f_{i}^2)}$  $sum5:\sum\limits_{i=l}^{r}{(2*val*y*f_{i+1}^2)}$
$sum3$的维护就不详细写了,可以看代码.
$sum6:\sum\limits_{i=l}^{r}{2*f_{i}*f_{i+1}*x}$  $sum7:\sum\limits_{i=l}^{r}{2*f_{i}*f_{i+1}*y}$
然后都维护一下就可以了.

#include<bits/stdc++.h>
#define ULL unsigned long long
using namespace std;
namespace FastIO {
    char buf[1<<21], *p1=buf, *p2=buf;
    inline int getch (void) {
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
    }
    inline int read (void) {
        int x = 0, f = 1, ch = getch();
        while(!isdigit(ch)) {
            if(ch == '-') f = -f;
            ch = getch();
        }
        while(isdigit(ch)) {
            x = x * 10 + ch - '0';
            ch = getch();
        }
        return x * f;
    }
    char buf2[1<<21], buf3[25];
    int tp_l, buf2_l=-1;
    inline void flush (void) {
        fwrite (buf2, 1, buf2_l+1, stdout), buf2_l=-1;
    }
    inline void print (int x, char ch=10) {
        if(buf2_l>(1<<20)) flush();
        if(x<0) buf2[++buf2_l]='-', x=-x;
        do buf3[++tp_l]=x%10+48;
        while(x/=10);
        do buf2[++buf2_l]=buf3[tp_l];
        while(--tp_l);
        buf2[++buf2_l] = ch;
    }
}
using FastIO::read;
using FastIO::print;
const int N=500005;
int n,m,id;
struct stu{
    ULL x,y;
    ULL sum1,sum2,sum3,sum4,sum5,sum6,sum7;
}t[500010*4];
ULL f[1001000],g1[1000050],g2[1000050],h[1000050];
#define f(i) f[i+N]
void addx(int k,int l,int r,ULL val){
    t[k].sum3=t[k].sum3+val*t[k].sum7;
    t[k].sum6=t[k].sum6+val*(h[r]-h[l-1]);
    t[k].sum1=t[k].sum1+val*val*(g1[r]-g1[l-1])+2*val*t[k].sum4;
    t[k].sum4=t[k].sum4+val*(g1[r]-g1[l-1]);
    t[k].x+=val;
}
void addy(int k,int l,int r,ULL val){
    t[k].sum3=t[k].sum3+val*t[k].sum6;
    t[k].sum7=t[k].sum7+val*(h[r]-h[l-1]);
    t[k].sum2=t[k].sum2+val*val*(g2[r]-g2[l-1])+2*val*t[k].sum5;
    t[k].sum5=t[k].sum5+val*(g2[r]-g2[l-1]);
    t[k].y+=val;
}
void pushdown(int k,int l,int r){
    int mid=(l+r)/2;
    if(t[k].x){
        addx(k*2,l,mid,t[k].x);
        addx(k*2+1,mid+1,r,t[k].x);
        t[k].x=0;
    }
    if(t[k].y){
        addy(k*2,l,mid,t[k].y);
        addy(k*2+1,mid+1,r,t[k].y);
        t[k].y=0;
    }
}
void pushup(int k){
    t[k].sum1=t[k*2].sum1+t[k*2+1].sum1;
    t[k].sum2=t[k*2].sum2+t[k*2+1].sum2;
    t[k].sum3=t[k*2].sum3+t[k*2+1].sum3;
    t[k].sum4=t[k*2].sum4+t[k*2+1].sum4;
    t[k].sum5=t[k*2].sum5+t[k*2+1].sum5;
    t[k].sum6=t[k*2].sum6+t[k*2+1].sum6;
    t[k].sum7=t[k*2].sum7+t[k*2+1].sum7;
}
void xg(int k,int l,int r,int L,int R,ULL x,ULL y){
    if(l>=L&&r<=R){
        addx(k,l,r,x);
        addy(k,l,r,y);
        return;
    }
    pushdown(k,l,r);
    int mid=(l+r)/2;
    if(L<=mid)xg(k*2,l,mid,L,R,x,y);
    if(R>mid)xg(k*2+1,mid+1,r,L,R,x,y);
    pushup(k);
}
ULL query(int k,int l,int r,int L,int R){
    if(l>=L&&r<=R){
        return t[k].sum1+t[k].sum2+2llu*t[k].sum3;
    }
    int mid=(l+r)/2;
    ULL res=0;
    pushdown(k,l,r);
    if(L<=mid)res=res+query(k*2,l,mid,L,R);
    if(R>mid)res=res+query(k*2+1,mid+1,r,L,R);
    return res;
}
int main(){
    // freopen("fib.in","r",stdin);
    // freopen("fib.out","w",stdout);
    f(1)=1;f(2)=1;
    for(int i=3;i<=500005;i++)f(i)=f(i-1)+f(i-2);
    for(int i=0;i>=-500005;i--)f(i)=f(i+2)-f(i+1);
    for(int i=1;i<=500003;i++){
        g1[i]=g1[i-1]+f(i)*f(i);
        g2[i]=g2[i-1]+f(i+1)*f(i+1);
        h[i]=h[i-1]+f(i)*f(i+1);
    }
    id=read();
    n=read();m=read();
    while(m--){
        int opt,l,r;
        opt=read();l=read();r=read();
        if(opt==1){
            xg(1,1,n,l,r,f(-l),f(1-l));
            continue;
        }
        if(opt==2){
            printf("%llu\n",query(1,1,n,l,r));
            continue;
        }
    }
    FastIO::flush();
    return 0;
}
View Code

 

集查并:

题意现在有一个并査集的顺序,要交换安排合并顺序和安排每次合并哪个为根,使得最后调用find的次数最少.
这题很抽象,反正我是不会.
我们发现操作会形成一棵树,每次操作就是把两个点合并成一个点,此时这两个点分别作为根的贡献是不同的,这个贡献可以用此时两个点的度来计算.
于是我们把题意转化为:

  • 我们先给每个点赋值$f_{i}=du_{i}-2$(为什么是$-2$就是凑出来的,因为合并两个点$f_{i}=du_{i}-2$和$f_{j}=du_{j}-2$就变成了一个新的$f_{new}=du_{new}-2$,形式相同更好维护).
  • 我们按照任意顺序合并两个连通块.
  • 每次合并的贡献是$max(f_{u},f_{v})$也就是安排根,把小的放在根上,大的接在下面,这样以后每次访问下面的都会产生了一个新的贡献.
  • 然后我们把新的点权赋值成$f_{u}+f_{v}$.
  • 我们要最大化贡献.


我们考虑把合并过的点染成黑色,可以证明每次我们一定是把一个黑色和一个白色合并.不可能把两个黑色合并,这一定是不优的.并且根的$f_{i}$一定比所有儿子的$f_{j}$大.

所以我们要模拟上述过程,我们钦定一个初始点已经被染成黑色了,那就是以这个点为根从上往下合并.因为第一次合并的值后面合并的时候会被反复计算$(n-1)$次...

  • 我们要把所有点排成一列,要求列中任意一个顶点都不能在它的祖先节点之前出现.
  • 最大化$\sum\limits_{i=1}^{n}{(n-i)*f_{i}}$.

这个问题就是https://loj.ac/p/2509,我们考虑如何安排顺序.
我们考虑在树上贪心,我们安排两个点的顺序会使得父亲连通块的$(n-i)$增加了$siz_{u}$,式子和上面这题一模一样,化简一下就是平均数.方法一样.

但是需要注意,因为我们为了方便计算把初始赋值$f_{i}=du_{i}-2$,少算了一些贡献所以我们最后要加上$3*(n-1)$,这可以理解成是凑出来的,很没道理/ng.

$3n-3=(n-1)+\sum\limits_{}^{}{du_{i}}$  $du是自己对自己的贡献,(n-1)是少算的贡献因为每次应该是du-1但是我们变成了du-2$

#include<bits/stdc++.h>
using namespace std;
int n,ans=0;
int du[2005],ff[2005],f[2005],siz[2005],fa[2005];
vector<int>p[2005];
struct stu{
    int id,f,siz;
    friend bool operator<(stu n1,stu n2){
        if(n1.f*n2.siz!=n2.f*n1.siz)return n1.f*n2.siz>n2.f*n1.siz;
        return n1.id<n2.id;
    }
};
set<stu>q;
int find(int x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
void dfs(int x,int F){
    ff[x]=F;
    for(int j=0;j<p[x].size();j++){
        int to=p[x][j];
        if(to==F)continue;
        dfs(to,x);
    }
}
int solve(int rt){
    int ans=0;
    memset(ff,0,sizeof(ff));
    dfs(rt,0);
    for(int i=1;i<=n;i++){
        f[i]=du[i]-2;siz[i]=1;fa[i]=i;
        if(i!=rt)q.insert((stu){i,f[i],siz[i]});
    }
    for(int i=1;i<n;i++){
        stu fi=*q.begin();
        q.erase(q.begin());
        int u=fi.id;
        int v=find(ff[u]),x=f[v],y=siz[v];
        fa[u]=v;ans+=siz[u]*f[v];
        siz[v]+=siz[u];f[v]+=f[u];
        if(v!=rt){
            q.erase((stu){v,x,y});
            q.insert((stu){v,f[v],siz[v]});
        }
    }
    return ans+3*n-3;
}
int main(){
    // freopen("dsu.in","r",stdin);
    // freopen("dsu.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int n1,n2;
        scanf("%d%d",&n1,&n2);
        n1++;n2++;
        p[n1].push_back(n2);
        p[n2].push_back(n1);
        du[n1]++;du[n2]++;
    }
    for(int i=1;i<=n;i++){
        bool flag=1;
        for(int j=0;j<p[i].size();j++){
            if(du[i]<du[j])flag=0;
        }
        if(flag==1)ans=max(solve(i),ans);
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

鲜花:

https://sy.hhwdd.com/new/ViewGProblem.page?gpid=D85pk
本题是一个$dp$,整个正方形我们可以按照对角线分成$4$个三角形区域,发现这些区域是相互独立的,也就是我们每次对其中一个$dp$加起来就是答案.
那么问题在于对角线上的我们会重复计算,而且一个$\dfrac{1}{4}$对角线只有一段前缀会被覆盖,所以我们枚举一个$\dfrac{1}{4}$对角线必须被哪个区域覆盖.
若正方形长度为奇数,那么中心有个点会被算$4$次,我们可以把这个点单独处理一下.

$dp$过程是设$f_{i,j,0/1}$表示到第$i$行,此时三角形的最大高度为$j$,这个三角形的趋势是上升还是下降,转移是平凡的.值的注意的是因为如果我们不用三角形覆盖,那么总的价值和就是$mc$,所以$j$的枚举范围是$\sqrt{mc}$.

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int n,m,c;
struct stu{
    int x,y;
}s[50005];
int f[2][505][2];
vector<int>p[50005];
void init(){
    auto cmp1=[&](stu n1,stu n2)->bool{
        if(n1.x!=n2.x)return n1.x<n2.x;
        return n1.y<n2.y;
    };
    sort(s+1,s+1+m,cmp1);
    auto cmp2=[&](stu n1,stu n2)->bool{
        return (n1.x==n2.x)&&(n1.y==n2.y);
    };
    m=unique(s+1,s+1+m,cmp2)-s-1;
    return;
}
int getans(){
    memset(f[0],inf,sizeof(f[0]));
    f[0][0][0]=0;
    for(int i=1;i<=n;i++){
        memset(f[i&1],inf,sizeof(f[i&1]));
        int pos=0,len=p[i].size(),mn=inf;
        for(int j=0;j<=500;j++){
            while(pos<len&&p[i][pos]<=j)pos++;
            mn=min(mn,min(f[(i-1)&1][j][0],f[(i-1)&1][j][1]));
            f[i&1][j][1]=min(f[i&1][j][1],mn+j*j+c*(len-pos));
            if(j)f[i&1][j][1]=min(f[i&1][j][1],f[(i-1)&1][j-1][1]+2*j-1+c*(len-pos));
            f[i&1][j][0]=min(f[i&1][j][0],min(f[(i-1)&1][j+1][0],f[(i-1)&1][j+1][1])+c*(len-pos));
        }
        p[i].clear();
    }
    return min(min(f[n&1][0][0],f[n&1][0][1]),min(f[n&1][1][0],f[n&1][1][1]));
}
pair<bool,bool> check(int x,int y,int p1,int p2,int id){
    bool ok1=0,ok2=0;
    if(id==1){
        if(p1){if(x<=y)ok1=1;}
        else {if(x<y)ok1=1;}
        if(p2){if(x<=n-y+1)ok2=1;}
        else {if(x<n-y+1)ok2=1;}
    }else if(id==2){
        if(p1){if(n-x+1<=y)ok1=1;}
        else {if(n-x+1<y)ok1=1;}
        if(p2){if(n-x+1<=n-y+1)ok2=1;}
        else {if(n-x+1<n-y+1)ok2=1;}
    }else if(id==3){
        if(p1){if(y<=x)ok1=1;}
        else {if(y<x)ok1=1;}
        if(p2){if(y<=n-x+1)ok2=1;}
        else {if(y<n-x+1)ok2=1;}
    }else {
        if(p1){if(n-y+1<=x)ok1=1;}
        else {if(n-y+1<x)ok1=1;}
        if(p2){if(n-y+1<=n-x+1)ok2=1;}
        else {if(n-y+1<n-x+1)ok2=1;}
    }

    return make_pair(ok1,ok2);
}
namespace sub1{
    int ans[5][2][2];
    void solve(){
        auto cmp1=[&](stu n1,stu n2)->bool{
            return n1.x<n2.x;
        };
        sort(s+1,s+1+m,cmp1);
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=1;k<=m;k++){
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,1);
                    if(ok.first&&ok.second)p[s[k].y].push_back(s[k].x);
                }
                ans[1][i][j]=getans();
            }
        }
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=m;k>=1;k--){
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,2);
                    if(ok.first&&ok.second)p[s[k].y].push_back(n-s[k].x+1);
                }
                ans[2][i][j]=getans();
            }
        }
        auto cmp2=[&](stu n1,stu n2)->bool{
            return n1.y<n2.y;
        };
        sort(s+1,s+1+m,cmp2);
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=1;k<=m;k++){
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,3);
                    if(ok.first&&ok.second)p[s[k].x].push_back(s[k].y);
                }
                ans[3][i][j]=getans();
            }
        }
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=m;k>=1;k--){
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,4);
                    if(ok.first&&ok.second)p[s[k].x].push_back(n-s[k].y+1);
                }
                ans[4][i][j]=getans();
            }
        }
        int ss=inf;
        for(int p1=0;p1<=1;p1++){
            for(int p2=0;p2<=1;p2++){
                for(int p3=0;p3<=1;p3++){
                    for(int p4=0;p4<=1;p4++){
                        int val=0;
                        val+=ans[1][p1][p2];
                        val+=ans[2][p3][p4];
                        val+=ans[3][p1^1][p3^1];
                        val+=ans[4][p2^1][p4^1];
                        ss=min(ss,val);
                    }
                }
            }
        }
        printf("%d\n",ss);
    }
}

namespace sub2{
    int ans[5][2][2];
    void solve(){
        int zdx=n/2+1,zdy=n/2+1;
        bool flag=0;
        for(int i=1;i<=m;i++){
            if(s[i].x==zdx&&s[i].y==zdy)flag=1;
        }

        auto cmp1=[&](stu n1,stu n2)->bool{
            return n1.x<n2.x;
        };
        sort(s+1,s+1+m,cmp1);
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=1;k<=m;k++){
                    if(s[k].x==zdx&&s[k].y==zdy)continue;
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,1);
                    if(ok.first&&ok.second)p[s[k].y].push_back(s[k].x);
                }
                ans[1][i][j]=getans();
            }
        }
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=m;k>=1;k--){
                    if(s[k].x==zdx&&s[k].y==zdy)continue;
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,2);
                    if(ok.first&&ok.second)p[s[k].y].push_back(n-s[k].x+1);
                }
                ans[2][i][j]=getans();
            }
        }
        auto cmp2=[&](stu n1,stu n2)->bool{
            return n1.y<n2.y;
        };
        sort(s+1,s+1+m,cmp2);
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=1;k<=m;k++){
                    if(s[k].x==zdx&&s[k].y==zdy)continue;
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,3);
                    if(ok.first&&ok.second)p[s[k].x].push_back(s[k].y);
                }
                ans[3][i][j]=getans();
            }
        }
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                for(int k=m;k>=1;k--){
                    if(s[k].x==zdx&&s[k].y==zdy)continue;
                    pair<bool,bool>ok=check(s[k].x,s[k].y,i,j,4);
                    if(ok.first&&ok.second)p[s[k].x].push_back(n-s[k].y+1);
                }
                ans[4][i][j]=getans();
            }
        }
        int ss=inf;
        for(int p1=0;p1<=1;p1++){
            for(int p2=0;p2<=1;p2++){
                for(int p3=0;p3<=1;p3++){
                    for(int p4=0;p4<=1;p4++){
                        int val=0;
                        val+=ans[1][p1][p2];
                        val+=ans[2][p3][p4];
                        val+=ans[3][p1^1][p3^1];
                        val+=ans[4][p2^1][p4^1];
                        ss=min(ss,val);
                    }
                }
            }
        }
        if(flag){
            ss=ss+c;
            for(int p1=0;p1<=1;p1++){
                for(int p2=0;p2<=1;p2++){
                    for(int p3=0;p3<=1;p3++){
                        for(int p4=0;p4<=1;p4++){
                            int val=0;
                            long long ls=zdx*zdx;
                            val+=ans[1][p1][p2];
                            val+=ans[2][p3][p4];
                            val+=ans[3][p1^1][p3^1];
                            val+=ans[4][p2^1][p4^1];
                            ss=min(ss*1ll,ls+val-ans[1][p1][p2]);
                            ss=min(ss*1ll,ls+val-ans[2][p3][p4]);
                            ss=min(ss*1ll,ls+val-ans[3][p1^1][p3^1]);
                            ss=min(ss*1ll,ls+val-ans[4][p2^1][p4^1]);
                        }
                    }
                }
            }
        }
        printf("%d\n",ss);
    }
}

int main(){
    // freopen("flower.in","r",stdin);
    // freopen("flower.out","w",stdout);
    scanf("%d%d%d",&n,&m,&c);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&s[i].x,&s[i].y);
    }
    init();
    if(n%2==0)sub1::solve();
    else sub2::solve();
    return 0;
}
View Code

 

posted @ 2023-06-26 17:41  星棋居  阅读(42)  评论(0)    收藏  举报