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;
}
集查并:
题意现在有一个并査集的顺序,要交换安排合并顺序和安排每次合并哪个为根,使得最后调用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;
}
鲜花:
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;
}


浙公网安备 33010602011771号