悬梯砸谭
[HNOI/AHOI2018]转盘
一个不太严谨的证明
如果固定起点,我们考虑接下来的一个点的\(T\)佷大,我肯定不会在这里停下来,转一圈明显更优
但是实际上如果我改变起点到这个点的后面的话明显答案更优,而且你会发现如果最开始的起点是\(1\)这样修改起点的操作是不断往前的,也就是不会反悔
由此我们可以枚举起点然后直接走只会转一圈
并且你会发现等待时间是可以直接挪动到起点去等的
先断环成链,我们设\(A_i=T_i-i\),也就是从\(1\)走要等待的时间
由此答案为\(Min_{i=1}^n(Max_{j=1}^{n}A_{i+j-1}+i)+n-1\)
这个\(j\)的求和上限是无所谓的,因为重复计算的一个点后面等待的时间明显更短
因此答案为\(Min_{i=1}^ni+P_i+n-1\),\(P_i\)为\(i\)的后缀最大值
额,还是不用\(P\)
\(Min_{i=1}^ni+Max_{j=i}^{}A_{j}+n-1\)
发现这个\(A\)我们只需要维护一个单调不增的单调栈而且答案就是这些单调栈里的元素做贡献
好像就完了,和楼房重建维护方式差不多
有点问题,如何保证取的大小\(>n\)?,额,直接取线段树左端点的答案即可
还有就是再哪统计答案,应该是每个元素位置后一个的位置作为答案,但实际上我们确定可以之间在划分区间时以\(mid\)为当前最优,可以证明对应的答案一定可以取到
Show Code
#define INF 1e9
#define ls 2*p
#define rs 2*p+1
using namespace std;
const int MAXN=2e5+5;
int n,m,p;
int T[MAXN];
int x,y;
struct Seg{
int date;
int Mx;
int l,r;
}Tree[MAXN*4];
int cal(int p,int x)
{
if(Tree[p].l==Tree[p].r)
{
if(Tree[p].l>n)
{
return INF;
}
else
{
return max(x,Tree[p].Mx)+Tree[p].l;
}
}
if(Tree[rs].Mx>=x)
{
return min(Tree[p].date,cal(rs,x));
}
else
{
int mid=(Tree[p].l+Tree[p].r)>>1;
return min(mid+x+1,cal(ls,x));
}
}
void Build(int p,int l,int r)
{
Tree[p].l=l;
Tree[p].r=r;
if(l==r)
{
Tree[p].Mx=T[l];
return;
}
int mid=(l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
Tree[p].Mx=max(Tree[ls].Mx,Tree[rs].Mx);
Tree[p].date=cal(ls,Tree[rs].Mx);
}
void Update(int p,int k,int x)
{
if(Tree[p].l==Tree[p].r)
{
Tree[p].Mx=x;
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(k<=mid)
{
Update(ls,k,x);
}
else
{
Update(rs,k,x);
}
Tree[p].Mx=max(Tree[ls].Mx,Tree[rs].Mx);
Tree[p].date=cal(ls,Tree[rs].Mx);
}
int main()
{
freopen("date.in","r",stdin);
scanf("%d %d %d",&n,&m,&p);
for(int i=1;i<=n;i++)
{
scanf("%d",&T[i]);
}
for(int i=1;i<=n;i++)
{
T[i+n]=T[i];
}
for(int i=1;i<=2*n;i++)
{
T[i]=T[i]-i;
}
Build(1,1,2*n);
int Last=n-1+Tree[1].date;
printf("%d\n",Last);
while(m--)
{
scanf("%d %d",&x,&y);
if(p==1)
{
x^=Last;
y^=Last;
}
Update(1,x,y-x);
Update(1,x+n,y-(x+n));
Last=n-1+Tree[1].date;
printf("%d\n",Last);
}
}
某题1
每次在现存\(n*m\)的网格图中的格子等概率选择一个格子并删去其右下角,为将所有格子删去步数的期望及期望的平方
首先\((1,1)\)是一定会选的,我们考虑将选择的路径拆分,每个点的贡献也即是他被选的概率\(P(i,j)\)
再考虑影响\((i,j)\)是否被选只与\((1,1)\)到\((i,j)\)有关,由此我们只用考虑\((i,j)\)左上的矩阵即可,所以\(P(i,j)=\dfrac{1}{ij}\)
再考虑平方怎么做,我们考虑把\(X^2\)拆成\(2\binom{X}{2}+X\)
由此我们只需要选择点对的概率即可
分讨一下,如果点对之间完全包含直接\(P\)相乘即可
如果不包含,考虑选择的先后顺序,对于两者选择区域的交\(Sum\),贡献即为\((\dfrac{1}{Sum})(\dfrac{1}{P_i}+\dfrac{1}{P_j})\)
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=998244353;
const int MAXN=1e7+5;
int Pow(int a,int b,int P)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%P;
}
base=((long long)base*base)%P;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int Inv[MAXN];
int fac[MAXN];
int inv_fac[MAXN];
int sum[505][505];
int n,m,type;
int main()
{
//freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d %d",&n,&m,&type);
int Res=0;
int Rn=0,Rm=0;
fac[0]=1;
for(int i=1;i<=MAXN-5;i++)
{
fac[i]=((long long)fac[i-1]*i)%MOD;
}
inv_fac[MAXN-5]=inv(fac[MAXN-5],MOD);
for(int i=MAXN-5-1;i>=1;i--)
{
inv_fac[i]=((long long)inv_fac[i+1]*(i+1))%MOD;
}
for(int i=1;i<=MAXN-5;i++)
{
Inv[i]=(((long long)inv_fac[i]*fac[i-1])%MOD);
}
for(int i=1;i<=n;i++)
{
Rn=((long long)Rn+Inv[i])%MOD;
}
for(int i=1;i<=m;i++)
{
Rm=((long long)Rm+Inv[i])%MOD;
}
if(type==0)
{
printf("%d\n",((long long)Rn*Rm)%MOD);
}
else
{
int E=((long long)Rn*Rm)%MOD;
int R=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
sum[i][j]=((long long)sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+MOD)%MOD;
R=((long long)R+((long long)sum[i][j]*Inv[i*j])%MOD)%MOD;
sum[i][j]=((long long)sum[i][j]+Inv[i*j])%MOD;
}
}
R=((long long)R*2)%MOD;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=1;k<i;k++)
{
int T=0;
int Tx=0;
for(int l=j+1;l<=m;l++)
{
Tx=((long long)Tx+Inv[i*j+k*(l-j)])%MOD;
T=((long long)T+((long long)Inv[i*j+k*(l-j)]*Inv[k*l])%MOD)%MOD;
}
T=((long long)T+((long long)Tx*Inv[i*j])%MOD)%MOD;
R=((long long)R+((long long)T*2)%MOD)%MOD;
}
}
}
printf("%d\n",((long long)R+E)%MOD);
}
}
某题2
有\(n\)个多项式,项数不超过\(k=7\),问\(n\)个多项式做或卷积的多项式,次数不超过\(2^{m=22}\)
首先直接做\(FWT\)的时间复杂度肯定不对
这里我们就算是求得每个多项式的点值表示相乘也得用\(2^{m}\)
考虑对单个多项式\(V_i\)做插值也就是求子集和
对于多项式里的项\(a_{i,j}\)为次数
我们枚举\(2^k\)求出\(V_i\)中每个值的最小子集
设\(S_=a_{i,1}|a_{i,2}\),不难发现其对最后乘积的贡献就是\(p_{i,1}+p_{i,2}\)
我们可以把子集和的部分省去,直接对最后的乘积做贡献
我们考虑维护\(dp_i\)为\(i\)对其超集所做贡献的乘法标记,似乎直接\(2^k\)打标记即可
但注意\(a_{i,1}\)会对\(S\)做\(p_{i,1}\),所以这里要容斥一下,即对\(a_{i,1}\)的超集除去对应贡献
最后一个前缀和加\(IFWT\)即可,注意会除\(0\)
Show Code
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
const int MAXN=1e5+5;
const int MOD=19260817;
int Pow(int a,int b,int P)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%P;
}
base=((long long)base*base)%P;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int P[MAXN][8];
int a[MAXN][8];
int n;
int m;
int k;
struct Number{
int val,cnt;
int invv;
Number operator*(const Number x)const{
int Vx=x.val;
Number rkd;
rkd.val=val;
rkd.cnt=cnt;
rkd.invv=invv;
rkd.cnt+=x.cnt;
rkd.val=((long long)rkd.val*Vx)%MOD;
rkd.invv=((long long)rkd.invv*x.invv)%MOD;
return rkd;
}
Number operator/(const Number x)const{
int Vx=x.val;
Number rkd;
rkd.val=val;
rkd.cnt=cnt;
rkd.invv=invv;
rkd.cnt-=x.cnt;
rkd.val=((long long)rkd.val*x.invv)%MOD;
rkd.invv=((long long)rkd.invv*x.val)%MOD;
return rkd;
}
};
Number dp[5000005];
Number Vpx[(1<<7)+5];
int V[5000005];
int main()
{
//freopen("date.in","r",stdin);
//freopen("date.out","w",stdout);
scanf("%d %d %d",&n,&m,&k);
int Invp=inv(1000000,MOD);
for(int i=0;i<(1<<m);i++)
{
dp[i]=((Number){1,0,1});
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<k;j++)
{
int x;
scanf("%d",&x);
P[i][j]=((long long)x*Invp)%MOD;
}
for(int j=0;j<k;j++)
{
scanf("%d",&a[i][j]);
}
for(int S=0;S<(1<<k);S++)
{
int Val=0;
for(int j=0;j<(k);j++)
{
if((S>>j)&1)
{
Val=((long long)Val+P[i][j])%MOD;
}
}
int Kr=0;
if(Val==0)
{
Val=1;
Kr++;
}
Vpx[S]=(Number){Val,Kr,inv(Val,MOD)};
}
for(int j=0;j<k;j++)
{
for(int S=0;S<(1<<k);S++)
{
if((S>>j)&1)
{
Vpx[S]=(Vpx[S]/Vpx[S^(1<<j)]);
}
}
}
for(int S=0;S<(1<<k);S++)
{
int Rs=0;
for(int j=0;j<k;j++)
{
if((S>>j)&1)
{
Rs=(Rs|a[i][j]);
}
}
dp[Rs]=(dp[Rs]*Vpx[S]);
}
}
for(int j=0;j<m;j++)
{
for(int S=0;S<(1<<m);S++)
{
if((S>>j)&1)
{
dp[S]=(dp[S]*dp[S^(1<<j)]);
}
}
}
for(int S=0;S<(1<<m);S++)
{
if(dp[S].cnt!=0)
{
V[S]=0;
}
else
{
V[S]=dp[S].val;
}
}
for(int l=2;l<=(1<<m);l<<=1)
{
for(int i=0;i<(1<<m);i+=l)
{
for(int j=i;j<i+(l>>1);j++)
{
int X=V[j];
int Y=V[j+(l>>1)];
V[j]=X;
V[j+(l>>1)]=((long long)Y-X+MOD)%MOD;
}
}
}
int Res=0;
for(int i=0;i<(1<<m);i++)
{
int Kx=V[i];
Kx=((long long)Kx*Pow(3,i,MOD))%MOD;
Res^=Kx;
}
printf("%d\n",Res);
}
[CCO2017] 接雨滴
首先对于一个局面,我们考虑向其中插入\(X\)
可以证明一定存在一种插法使得局面的雨滴数不变
原因:我们考虑如果可以找到一个上方没有雨滴的\(Y\)且\(Y\)是最大的低于\(X\)的柱
再找到一个\(Z\)没有雨滴且高于\(Y\),\(Z\)是一定可以找到的,因为最高的柱是不会有雨滴的
然后考虑把\(X\)插在\(Y,Z\)中间,那\(X\)一定不会产生雨滴,同时如果\(X\)紧接\(Z\),那中间的柱的雨滴还是由\(Y\)决定
如果找不到这样的\(Y\),说明\(X\)直接插在边界即可
如果我们从高到矮插入
然后对于插入的柱,对于除了最高的柱,都是可以产生\(h_j-h_i\)的贡献
因为我们可以用\(i\)将\(j\)替换,这样的贡献就有\(h_j-h_i\),最后\(j\)以上述方式插入即可
最后直接\(bitset\)优化\(dp\)即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=250005;
int n;
int h[505];
bitset<MAXN>S,T;
int cmp(int x,int y)
{
return x>y;
}
int main()
{
freopen("date.in","r",stdin);
freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&h[i]);
}
sort(h+1,h+1+n,cmp);
S.reset();
S.set(0);
for(int i=2;i<=n;i++)
{
T=S;
for(int j=2;j<i;j++)
{
S|=(T<<(h[j]-h[i]));
}
//cout<<S<<endl;
}
for(int i=0;i<=MAXN-5;i++)
{
if(S[i])
{
printf("%d ",i);
}
}
}
CF755G PolandBall and Many Other Balls
不难列出递推式\(dp_{i,j}=dp_{i-1,j-1}+dp_{i-1,j}+dp_{i-2,j-1}\)
然后我们设\(f_i(x)=\sum\limits_{j=0}dp_{i,j}x^j\)
很明显\(f_i(x)=f_{i-1}(x)(x+1)+f_{i-2}(x)x\)
然后矩阵就可以了,好像插值不可以把,只能把多项式代进去算
换一种思路,对于一个长为\(a,b\)的序列,把他们拼接在一起,要么中间合成一组,或者\(a,b\)独立,则\(f_{a+b}(x)=f_{a}(x)f_{b}(x)+xf_{a-1}(x)f_{b-1}(x)\)
利用这个,我们可以得出\(f_{2n}(x)=f_n^2(x)+f_{n-1}^{2}(x)x\)
\(f_{2n-1}(x)=f_n(x)f_{n-1}(x)+f_{n-2}(x)f_{n-1}(x)x\)
\(f_{2n-2}(x)=f_{n-1}^2(x)+f_{n-2}^{2}(x)x\)
维护这三个,具体的我们可以得到\(n=2n,n=n+1\)的操作
直接对模拟\(n\)的取得即可
注意多项式乘法对\(x^{k+1}\)取模,长度应该是\(2k\),且每次计算后得舍去\(>k\)的
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=998244353;
const int g=3;
const int MAXN=4e5+5;
int Rev[MAXN];
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
struct Poly{
vector<int>V;
int size()
{
return V.size();
}
void clear()
{
V.clear();
return;
}
void push_back(int x)
{
V.push_back(x);
return;
}
void NTT(int Limit,int type)
{
int Len=(1<<Limit);
for(int i=0;i<Len;i++)
{
Rev[i]=((Rev[i>>1]>>1)|((i&1)<<(Limit-1)));
}
while(V.size()<Len)
{
V.push_back(0);
}
for(int i=0;i<Len;i++)
{
if(i<Rev[i])
{
swap(V[i],V[Rev[i]]);
}
}
for(int l=1;l<Len;l<<=1)
{
int Wn=Pow(g,(MOD-1)/(l<<1),MOD);
if(type==-1)
{
Wn=inv(Wn,MOD);
}
for(int i=0;i<Len;i+=(l<<1))
{
int W=1;
for(int j=i;j<i+l;j++,W=((long long)W*Wn)%MOD)
{
int Xc=V[j];
int Yc=((long long)V[j+l]*W)%MOD;
V[j]=((long long)Xc+Yc)%MOD;
V[j+l]=((long long)Xc-Yc+MOD)%MOD;
}
}
}
if(type==-1)
{
int Liv=inv(Len,MOD);
for(int i=0;i<Len;i++)
{
V[i]=((long long)V[i]*Liv)%MOD;
}
}
}
};
int n;
int k;
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&k);
int nt=0;
int nw=1;
while(nw<=2*(k+10))
{
nw<<=1;
nt++;
}
Poly A,B,C;
A.clear();
A.V.resize(nw,0);
A.V[0]=1;
A.V[1]=1;
B.clear();
B.V.resize(nw,0);
B.V[0]=1;
C.clear();
C.V.resize(nw,0);
C.V[0]=0;
for(int i=log2(n)-1;i>=0;i--)
{
Poly Ax,Bx,Cx,Dx,Ex;
Ax.clear();
Bx.clear();
Cx.clear();
Dx.clear();
Ex.clear();
Ax.V.resize(nw,0);
Bx.V.resize(nw,0);
Cx.V.resize(nw,0);
Dx.V.resize(nw,0);
Ex.V.resize(nw,0);
A.NTT(nt,1);
B.NTT(nt,1);
C.NTT(nt,1);
for(int j=0;j<nw;j++)
{
Ax.V[j]=((long long)A.V[j]*A.V[j])%MOD;//nn
Bx.V[j]=((long long)B.V[j]*A.V[j])%MOD;//n(n-1)
Cx.V[j]=((long long)B.V[j]*B.V[j])%MOD;//(n-1)(n-1)
Dx.V[j]=((long long)B.V[j]*C.V[j])%MOD;//(n-1)(n-2)
Ex.V[j]=((long long)C.V[j]*C.V[j])%MOD;//(n-2)(n-2)
}
Ax.NTT(nt,-1);
Bx.NTT(nt,-1);
Cx.NTT(nt,-1);
Dx.NTT(nt,-1);
Ex.NTT(nt,-1);
//printf("%d %d???\n",Ax.V[0],Cx.V[0]);
for(int j=0;j<nw;j++)
{
A.V[j]=Ax.V[j];
if(j)
{
A.V[j]=((long long)A.V[j]+Cx.V[j-1])%MOD;
}
B.V[j]=Bx.V[j];
if(j)
{
B.V[j]=((long long)B.V[j]+Dx.V[j-1])%MOD;
}
C.V[j]=Cx.V[j];
if(j)
{
C.V[j]=((long long)C.V[j]+Ex.V[j-1])%MOD;
}
}
if((n>>i)&1)
{
for(int j=0;j<nw;j++)
{
Ax.V[j]=A.V[j];
if(j)
{
Ax.V[j]=((long long)Ax.V[j]+A.V[j-1])%MOD;
Ax.V[j]=((long long)Ax.V[j]+B.V[j-1])%MOD;
}
Bx.V[j]=A.V[j];
Cx.V[j]=B.V[j];
}
//printf("fuck\n");
A=Ax;
B=Bx;
C=Cx;
}
for(int j=k+1;j<nw;j++)
{
A.V[j]=0;
}
for(int j=k+1;j<nw;j++)
{
B.V[j]=0;
}
for(int j=k+1;j<nw;j++)
{
C.V[j]=0;
}
// printf("%d::\n",i);
// for(int j=0;j<nw;j++)
// {
// printf("%d ",A.V[j]);
// }
// printf("\n");
// for(int j=0;j<nw;j++)
// {
// printf("%d ",B.V[j]);
// }
// printf("\n");
// for(int j=0;j<nw;j++)
// {
// printf("%d ",C.V[j]);
// }
// printf("\n");
}
for(int i=1;i<=k;i++)
{
printf("%d ",A.V[i]);
}
}
【集训队互测2015】最大异或和
首先差个分,肯定对线性基没影响
然后操作\(1\)肯定是直接改\(l,r+1\)
对于操作\(2\),我们要删除\([l+1,r]\),但线性基单次删除是\(\dfrac{m^2}{w}\)
实际上这里删的次数不会超过\((n+q)\)次,因为删过的不会再删,然后也只有操作\(1\)会使得它重新有值,但一共也只有\(q\)次
总时间\(O(\dfrac{m^3}{w})\)
这里补一个线性基删除
对于目前要删的\(x\),如果它没在线性基里就不用管
如果在的话,我们可以维护一下每个数用线性基内的数的表示方式,同时维护一下基里的数用原数的表示方式
然后我们会发现如果一个未加入线性基的\(Y\)可以被\(X\)表示,那\(Y\)便可以替换\(X\),同时基不变
否则我们必须删除一个基,这里找最小的即可,同时其他数也得去除它得贡献
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2005;
int n,m,q;
bitset<MAXN>A[MAXN],C[MAXN],B[MAXN];
bitset<MAXN>S[MAXN],T[MAXN],Tmp;
int op,x,y;
int l,r;
int In[MAXN];
void Insert(int x)
{
C[x]=A[x]^A[x-1];
for(int i=m-1;i>=0;i--)
{
if(C[x][i])
{
if(B[i][i])
{
C[x]^=B[i];
S[x]^=T[i];
}
else
{
B[i]=C[x];
T[i]=S[x];
In[x]=1;
T[i].set(x);
break;
}
}
}
}
void Delete(int x)
{
if(x>n)
{
return;
}
C[x]=A[x]^A[x-1];
if(C[x].none())
{
return;
}
if(!In[x])
{
S[x].reset();
return;
}
for(int i=1;i<=n;i++)
{
if((!In[i])&&(S[i][x]))
{
In[i]=1;
In[x]=0;
S[i].set(i);
S[x].reset();
for(int j=1;j<=n;j++)
{
if(S[j][x]&&(!In[j]))
{
S[j]^=S[i];
}
}
for(int j=m-1;j>=0;j--)
{
if(T[j][x])
{
T[j]^=S[i];
}
}
S[i].reset(x);
return;
}
}
In[x]=0;
int z;
for(int i=0;i<m;i++)
{
if(T[i][x])
{
z=i;
break;
}
}
for(int i=z+1;i<m;i++)
{
if(T[i][x])
{
T[i]^=T[z];
B[i]^=B[z];
}
}
T[z].reset();
B[z].reset();
S[x].reset();
}
int main()
{
//freopen("date.in","r",stdin);
//freopen("date.out","w",stdout);
scanf("%d %d %d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
cin>>A[i];
Insert(i);
}
while(q--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d %d",&l,&r);
cin>>Tmp;
Delete(l);
Delete(r+1);
for(int i=l;i<=r;i++)
{
A[i]^=Tmp;
}
Insert(l);
Insert(r+1);
}
else if(op==2)
{
scanf("%d %d",&l,&r);
cin>>Tmp;
for(int i=l;i<=r+1;i++)
{
Delete(i);
}
for(int i=l;i<=r;i++)
{
A[i]=Tmp;
}
Insert(l);
Insert(r+1);
}
else
{
Tmp.reset();
for(int i=m-1;i>=0;i--)
{
if(B[i][i]&&(!Tmp[i]))
{
Tmp^=B[i];
}
}
for(int i=m-1;i>=0;i--)
{
cout<<Tmp[i];
}
printf("\n");
}
}
}
CF1500F Cupboards Jumps
一个不难想到的思路是考虑前两个数有了构造第三个数
然后你会发现第三个数的\(B\)一定是得大于前两个数的差值的,不然明显构造不出
事实上第三个数只能放在中间或者两边,且两边的位置是固定的
但实际上这里不能贪心地使两数距离最小,得\(dp\)
这里设\(dp_{i,j}\)为前\(i\)个数\(i-1,i-2\)的距离为\(j\)时的可达性
不难得到\(j=B\),则\(j'\in[0,B]\)
\(j>B\),则\(j'=B/B-j\)
可以发现我们的\(j\)要么是一段区间要么就是不超过\(i\)个单点,因为每次只会插一个\(B\)
我们只需用\(deque\)模拟即可,输出方案就随便取一个\(j\)即可
实际上倒着推方案是个很难的事,我们必须记录每个过程任意一个单点和区间来模拟
Show Code
using namespace std;
long long Abs(int x)
{
return x>0?x:-x;
}
const int MAXN=1e6+5;
int n;
long long B;
long long b[MAXN];
long long L,R;
deque<pair<long long,int>>S;
int op;
long long deta=0;
long long Rs[MAXN];
long long D[MAXN];
int Ox[MAXN];
long long Dl[MAXN];
long long Dr[MAXN];
pair<long long,int> Ds[MAXN];
pair<long long,int> Dx[MAXN];
long long Res[MAXN];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %lld",&n,&B);
for(int i=3;i<=n;i++)
{
scanf("%lld",&b[i]);
}
L=0;
R=b[3];
S.clear();
op=1;
deta=0;
Dl[2]=L;
Dr[2]=R;
Ds[2]=make_pair(-1,0);
Ox[2]=1;
D[2]=0;
for(int i=3;i<=n;i++)
{
long long B=(b[i]-deta)*op;
bool found=0;
bool F=0;
if(op==1)
{
while(S.size()&&S.back().first>B)
{
S.pop_back();
}
if(S.size()&&S.back().first==B)
{
found=1;
Dx[i]=S.back();
}
R=min(R,B);
if(B>=L&&B<=R)
{
found=1;
}
op*=-1;
deta*=-1;
deta+=b[i];
}
else
{
while(S.size()&&S.front().first<B)
{
S.pop_front();
}
if(S.size()&&S.front().first==B)
{
found=1;
Dx[i]=S.front();
}
L=max(L,B);
if(B>=L&&B<=R)
{
found=1;
}
op*=-1;
deta*=-1;
deta+=b[i];
}
if((S.size()||(L<=R)))
{
F=1;
}
Dl[i]=L;
Dr[i]=R;
if(S.size())
{
Ds[i]=S.front();
}
else
{
Ds[i]=make_pair(-1,-1);
}
if(found)
{
if(op==1)
{
long long Txl=(0-deta)*op;
long long Txr=(b[i]-deta)*op;
L=min(L,Txl);
R=max(R,Txr);
}
else
{
long long Txr=(0-deta)*op;
long long Txl=(b[i]-deta)*op;
L=min(L,Txl);
R=max(R,Txr);
}
}
if(F)
{
if(op==1)
{
S.push_back(make_pair((b[i]-deta)*op,i));
}
else
{
S.push_front(make_pair((b[i]-deta)*op,i));
}
}
D[i]=deta;
Ox[i]=op;
}
if(L<=R||(S.size()))
{
printf("YES\n");
pair<long long,int>Rx;
if(L<=R)
{
Rx.first=L;
Rx.second=-1;
}
else
{
Rx=S.front();
}
int OO=1;
for(int i=n;i>=2;i--)
{
Rs[i]=((Rx.first*Ox[i])+D[i])*OO;
if(i==2)
{
break;
}
if(Rx.second==-1)
{
long long Rl=Dl[i];
long long Rr=Dr[i];
long long B=(b[i]-D[i-1])*Ox[i-1];
if(Rx.first>=Rl&&Rx.first<=Rr)
{
}
else
{
if(Rl<=B&&B<=Rr)
{
Rx.first=B;
Rx.second=-1;
}
else
{
Rx=Dx[i];
}
OO*=-1;
}
}
else if(Rx.second==i)
{
if(Ds[i].second!=-1)
{
Rx=Ds[i];
}
else
{
Rx.first=Dl[i];
Rx.second=-1;
}
OO*=-1;
}
}
Res[1]=0;
long long Now=0;
for(int i=2;i<=n;i++)
{
Now+=Rs[i];
Res[i]=Now;
}
long long Mini=0;
for(int i=1;i<=n;i++){
Mini=min(Mini,Res[i]);
}
for(int i=1;i<=n;i++)
{
printf("%lld ",Res[i]-Mini);
}
}
else
{
printf("NO\n");
}
}
[省选联考 2023] 人员调度
暴力的做法就是用可并堆一个一个合并,保存当前子树可行的人员,这里要保证可并堆的大小小于等于子树大小即可
我们在这里记录被选入最终答案的人并在节点上记录它子树内的人,很明显没选入的以后也不会被选入
考虑这里我们新添一个点的贡献
如果它到根的所有节点都没有满,那直接把它加进去即可
否则,我们考虑它上面第一个满的节点\(u\),并让它和这个节点中的最小值替换
这样作出来的答案一定是目前最优的
同时考虑如果这个被换下来的最小值如果可以在后续答案出现,那我加进去的也一定能出现,因为两个人在\(u\)处冲突,肯定是选权值更大的好
对于权值相同的,我们随便替换即可,因为两者的地位可以看成是等价的,如果两次被删肯定第一次随便删,如果只删一次说明\(u\)只被访问了一次
当然,这个维护用树剖维护每个节点的空闲大小,然后二分一下即可
对于带删,我们直接用线段树分治即可
时间复杂度\(O(nlog^3(n))\)
Show Code
</pre> </details>
#include<bits/stdc++.h>
#define ls 2*p
#define rs 2*p+1
using namespace std;
const int MAXN=1e5+5;
int n,k,q;
int sid;
vector<int>g[MAXN];
int fa[MAXN];
int cnt_node=0;
struct People{
int u,val;
int L,R;
}a[MAXN*2];
int op;
int x,v;
int Siz[MAXN];
int wson[MAXN];
int dfn[MAXN];
int cnt_dfn;
int top[MAXN];
int Rlf[MAXN];
int dep[MAXN];
void dfs1(int x,int f)
{
Siz[x]=1;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
fa[v]=x;
dep[v]=dep[x]+1;
dfs1(v,x);
Siz[x]+=Siz[v];
if(Siz[v]>Siz[wson[x]])
{
wson[x]=v;
}
}
}
void dfs2(int x,int Top)
{
top[x]=Top;
dfn[x]=++cnt_dfn;
Rlf[cnt_dfn]=x;
if(wson[x])
{
dfs2(wson[x],Top);
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==wson[x])
{
continue;
}
if(v==fa[x])
{
continue;
}
dfs2(v,v);
}
}
struct Seg_node{
int sdate;
int l,r;
int lazy;
pair<int,int>mdate;
};
struct Seg{
Seg_node Tree[MAXN*4];
multiset<pair<int,int> >S[MAXN];
void push_up(int p)
{
Tree[p].sdate=min(Tree[ls].sdate,Tree[rs].sdate);
Tree[p].mdate=min(Tree[ls].mdate,Tree[rs].mdate);
}
void push_down(int p)
{
if(Tree[p].lazy!=0)
{
Tree[ls].lazy+=Tree[p].lazy;
Tree[rs].lazy+=Tree[p].lazy;
Tree[ls].sdate+=Tree[p].lazy;
Tree[rs].sdate+=Tree[p].lazy;
Tree[p].lazy=0;
}
}
void Build(int p,int l,int r)
{
Tree[p].l=l;
Tree[p].r=r;
Tree[p].lazy=0;
Tree[p].mdate=make_pair(0x3f3f3f3f,-1);
if(l==r)
{
Tree[p].sdate=(Siz[Rlf[l]]);
return;
}
int mid=(l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
push_up(p);
}
void Updatem(int p,int k,pair<int,int>X,int op)
{
if(Tree[p].l==Tree[p].r)
{
if(op==1)
{
S[Tree[p].l].insert(X);
}
else
{
S[Tree[p].l].erase(S[Tree[p].l].find(X));
}
if(S[Tree[p].l].size())
{
multiset<pair<int,int> >::iterator it=S[Tree[p].l].begin();
Tree[p].mdate=(*it);
}
else
{
Tree[p].mdate=make_pair(0x3f3f3f3f,-1);
}
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
push_down(p);
if(k<=mid)
{
Updatem(ls,k,X,op);
}
else
{
Updatem(rs,k,X,op);
}
push_up(p);
}
pair<int,int> Querym(int p,int l,int r)
{
if(Tree[p].l>=l&&Tree[p].r<=r)
{
return Tree[p].mdate;
}
push_down(p);
int mid=(Tree[p].l+Tree[p].r)>>1;
pair<int,int>Res=make_pair(0x3f3f3f3f,-1);
if(l<=mid)
{
Res=min(Res,Querym(ls,l,r));
}
if(r>mid)
{
Res=min(Res,Querym(rs,l,r));
}
return Res;
}
void Updates(int p,int l,int r,int k)
{
if(Tree[p].l>=l&&Tree[p].r<=r)
{
Tree[p].sdate+=k;
Tree[p].lazy+=k;
return;
}
push_down(p);
int mid=(Tree[p].l+Tree[p].r)>>1;
if(l<=mid)
{
Updates(ls,l,r,k);
}
if(r>mid)
{
Updates(rs,l,r,k);
}
push_up(p);
}
int Querys(int p,int l,int r)
{
if(Tree[p].l>=l&&Tree[p].r<=r)
{
return Tree[p].sdate;
}
push_down(p);
int mid=(Tree[p].l+Tree[p].r)>>1;
int Res=0x3f3f3f3f;
if(l<=mid)
{
Res=min(Res,Querys(ls,l,r));
}
if(r>mid)
{
Res=min(Res,Querys(rs,l,r));
}
return Res;
}
}tree;
void Update_chain(int x,int y,int k)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
{
swap(x,y);
}
tree.Updates(1,dfn[top[x]],dfn[x],k);
x=fa[top[x]];
}
if(dep[x]<dep[y])
{
swap(x,y);
}
tree.Updates(1,dfn[y],dfn[x],k);
return;
}
int Query_chain(int x)
{
while(top[x]!=1)
{
if(tree.Querys(1,dfn[top[x]],dfn[x])>0)
{
x=fa[top[x]];
}
else
{
break;
}
}
if(tree.Querys(1,dfn[top[x]],dfn[x])>0)
{
return 0;
}
int l=dfn[top[x]];
int r=dfn[x];
int Key=dfn[x];
while(l<=r)
{
int mid=(l+r)>>1;
if(tree.Querys(1,mid,dfn[x])<=0)
{
l=mid+1;
Key=mid;
}
else
{
r=mid-1;
}
}
return Rlf[Key];
}
struct Segq_node{
int l,r;
vector<People>V;
};
long long Ans[MAXN];
struct Qs{
pair<int,int>R;
int La;
};
struct Segq{
Segq_node Tree[MAXN*4];
long long Res=0;
void Build(int p,int l,int r)
{
Tree[p].l=l;
Tree[p].r=r;
Tree[p].V.clear();
if(l==r)
{
return;
}
int mid=(l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
}
void Update(int p,int l,int r,People x)
{
if(Tree[p].l>=l&&Tree[p].r<=r)
{
Tree[p].V.push_back(x);
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(l<=mid)
{
Update(ls,l,r,x);
}
if(r>mid)
{
Update(rs,l,r,x);
}
}
stack<pair<Qs,Qs> >st;
void dfs(int p)
{
int Sx=st.size();
for(int i=0;i<Tree[p].V.size();i++)
{
int u=Tree[p].V[i].u;
int Li=Query_chain(u);
pair<int,int>Nx=make_pair(Tree[p].V[i].val,u);
//printf("%d %d %d??\n",Nx.second,Nx.first,Li);
if(Li==0)
{
Update_chain(u,1,-1);
tree.Updatem(1,dfn[u],Nx,1);
Res+=Nx.first;
st.push(make_pair((Qs){Nx,1},(Qs){Nx,-1}));
}
else
{ pair<int,int>Tx=tree.Querym(1,dfn[Li],dfn[Li]+Siz[Li]-1);
if(Tx.first<Nx.first)
{
Update_chain(u,Li,-1);
tree.Updatem(1,dfn[u],Nx,1);
Res+=Nx.first;
Update_chain(Tx.second,Li,1);
tree.Updatem(1,dfn[Tx.second],Tx,-1);
Res-=Tx.first;
st.push(make_pair((Qs){Nx,Li},(Qs){Tx,Li}));
}
}
}
if(Tree[p].l==Tree[p].r)
{
Ans[Tree[p].l]=Res;
while(st.size()>Sx)
{
pair<Qs,Qs>temp=st.top();
st.pop();
Update_chain(temp.first.R.second,temp.first.La,1); tree.Updatem(1,dfn[temp.first.R.second],temp.first.R,-1);
Res-=temp.first.R.first;
if(temp.second.La!=-1)
{ Update_chain(temp.second.R.second,temp.second.La,-1); tree.Updatem(1,dfn[temp.second.R.second],temp.second.R,1);
Res+=temp.second.R.first;
}
}
return;
}
dfs(ls);
dfs(rs);
while(st.size()>Sx)
{
pair<Qs,Qs>temp=st.top();
st.pop();
Update_chain(temp.first.R.second,temp.first.La,1);
tree.Updatem(1,dfn[temp.first.R.second],temp.first.R,-1);
Res-=temp.first.R.first;
if(temp.second.La!=-1)
{ Update_chain(temp.second.R.second,temp.second.La,-1); tree.Updatem(1,dfn[temp.second.R.second],temp.second.R,1);
Res+=temp.second.R.first;
}
}
}
}Tq;
int main()
{
freopen("date.in","r",stdin);
freopen("date.out","w",stdout);
scanf("%d",&sid);
scanf("%d %d %d",&n,&k,&q);
for(int i=2;i<=n;i++)
{
scanf("%d",&x);
g[x].push_back(i);
g[i].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=k;i++)
{
++cnt_node;
scanf("%d %d",&a[cnt_node].u,&a[cnt_node].val);
a[cnt_node].L=0;
a[cnt_node].R=q;
}
for(int i=1;i<=q;i++)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d %d",&x,&v);
++cnt_node;
a[cnt_node].u=x;
a[cnt_node].val=v;
a[cnt_node].L=i;
a[cnt_node].R=q;
}
else
{
scanf("%d",&x);
a[x].R=i-1;
}
}
Tq.Build(1,0,q);
tree.Build(1,1,n);
for(int i=1;i<=cnt_node;i++)
{
Tq.Update(1,a[i].L,a[i].R,a[i]);
}
Tq.dfs(1);
for(int i=0;i<=q;i++)
{
printf("%lld ",Ans[i]);
}
}
[PA 2022] Wieczór gier
降智了
如果我们只有一个点,我们对局面建图,建出来肯定是个网格图
考虑对它黑白染色,可以证明没有奇环
然后我们可以直接判无解
然后由于走的步数很大,我们这里设\(p_i\)为最后停到\(i\)的概率
然后你会发现\(p_i\)的值其实就是\(i\)的度在所有黑色点的度之和所占的比例
对于分子,我们可以直接对终止状态暴力
对于分母,我们可以发现这就是所有的边,也即\((n-1)m+(m-1)n\)
然后如果不止一个点,判无解就是每个点的黑白总数不变
分子直接算
对于分母,我们可以发现照样是局面中每个棋子的度数之和,然后分到边上,我们可以发现每条边只会经过\(\binom{nm-2}{k-1}\),\(k\)是棋子数
然后用\(double\)算就行了
Show Code
</pre> </details>
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
int n,m;
int mp1[15][15];
int mp2[15][15];
char s[105];
short zfx[5]={0,0,1,-1};
short zfy[5]={1,-1,0,0};
double C(int n,int m)
{
double R=1;
for(int i=m+1;i<=n;i++)
{
R*=i;
}
for(int i=1;i<=n-m;i++)
{
R/=i;
}
return R;
}
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%llu %llu",&n,&m);
int R1=0;
int R2=0;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++)
{
if(s[j]=='O')
{
mp1[i][j]=1;
R1+=i;
R1+=j;
R1%=2;
}
}
}
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
{
if(s[j]=='O')
{
mp2[i][j]=1;
R2+=i;
R2+=j;
R2%=2;
}
}
}
// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=m;j++)
// {
// printf("%llu",mp1[i][j]);
// }
// }
// printf("\n");
// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=m;j++)
// {
// printf("%llu",mp2[i][j]);
// }
// }
// printf("\n");
if(R1!=R2)
{
printf("0");
}
else
{
int K=0;
int Rs=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(!mp2[i][j])
{
continue;
}
++K;
for(int k=0;k<4;k++)
{
int nx=i+zfx[k];
int ny=j+zfy[k];
if(nx>=1&&ny>=1&&nx<=n&&ny<=m)
{
if(!mp2[nx][ny])
{
Rs++;
}
}
}
}
}
double Rp=((n-1)*m+(m-1)*n);
Rp=(Rp*C(n*m-2,K-1));
printf("%.15lf\n",Rs*1.0/Rp);
}
}
[PA 2022] Nawiasowe podziały
一眼\(wqs\)二分
下凸性感性理解一下吧,不会证
然后考虑二分了一个斜率\(K\),我们这里求一个划分任意段的最小值
\(dp_i=dp_j+Cost(j+1,i)-K\)
然后\(Cost(j,i)\)明显满足四边形不等式,这里就可以直接用决策单调性优化
对于\(Cost(j+1,i)\)的计算,可以用莫队,其实挺有意思的
我们发现在右边加一个\()\)实际上的贡献就是它匹配到的\((\)编号\(i\)左边有多少个连续的类似\(()(())()...+1\)
然后我们发现这东西\(i-1\)同样有这样的过程
然后考虑这个玩意可以染个色,只给右括号染,然后就莫队统计
对了,这个地方如果用莫队的话,必须用分治,但分治的话不能有后效性,这里我们就必须在外面套一层\(CDQ\)
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n,m;
char s[MAXN];
long long Tot=0;
long long K;
int Ll=1,Rr=0;
bool operator<(pair<long long,int>x,pair<long long,int>y)
{
if(x.first==y.first)
{
return (x.second>y.second);
}
return x.first<y.first;
}
int col[MAXN];
int C[MAXN];
long long Cost(int L,int R)
{
L--;
while(Rr<R)
{
++Rr;
Tot+=C[col[Rr]];
C[col[Rr]]++;
}
while(Ll>L)
{
Ll--;
Tot+=C[col[Ll]];
C[col[Ll]]++;
}
while(Rr>R)
{
C[col[Rr]]--;
Tot-=C[col[Rr]];
Rr--;
}
while(Ll<L)
{
C[col[Ll]]--;
Tot-=C[col[Ll]];
Ll++;
}
return Tot;
}
int P[MAXN];
pair<long long,int> dp[MAXN];
void solve(int l,int r,int L,int R)
{
if(l>r)
{
return;
}
if(L==R)
{
for(int i=l;i<=r;i++)
{
pair<long long,int>Nw=make_pair(dp[L].first+Cost(L+1,i)-K,dp[L].second+1);
if(Nw<dp[i])
{
dp[i]=Nw;
P[i]=L;
}
}
return;
}
int mid=(l+r)>>1;
int Pmid=L;
pair<long long,int>Mda=make_pair(dp[L].first+Cost(L+1,mid)-K,dp[L].second+1);
for(int i=L+1;i<=R;i++)
{
pair<long long,int>Nw=make_pair(dp[i].first+Cost(i+1,mid)-K,dp[i].second+1);
if(Nw<Mda)
{
Mda=Nw;
Pmid=i;
}
}
if(Mda<dp[mid])
{
dp[mid]=Mda;
P[mid]=Pmid;
}
solve(l,mid-1,L,Pmid);
solve(mid+1,r,Pmid,R);
}
void CDQ(int l,int r)
{
if(l==r)
{
if(l==0)
{
dp[0]=make_pair(0,0);
}
return;
}
int mid=(l+r)>>1;
CDQ(l,mid);
solve(mid+1,r,l,mid);
CDQ(mid+1,r);
}
pair<long long,int>Get(long long mid)
{
K=mid;
for(int i=0;i<=n;i++)
{
dp[i].first=1e18;
dp[i].second=0;
}
CDQ(0,n);
return dp[n];
}
stack<int>st;
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&m);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
if(s[i]=='(')
{
col[i]=i;
st.push(i);
}
else
{
if(st.size())
{
col[i]=col[st.top()-1];
st.pop();
}
else
{
col[i]=i;
}
}
}
long long l=-1e18;
long long r=1e18;
long long Key;
while(l<=r)
{
long long mid=(l+r)>>1ll;
pair<long long,int>Rx=Get(mid);
if(Rx.second>=m)
{
Key=mid;
r=mid-1;
}
else
{
l=mid+1;
}
}
pair<long long,int>Rx=Get(Key);
printf("%lld",Rx.first+m*Key);
}
[八省联考 2018] 林克卡特树
又一个\(wqs\)二分
首先证明上凸性不会
口胡一下,就是考虑你\(K\)很小的时候就是树的直径,\(K\)增大就会删一些负权边或者接一下直径
直到最后得到的值最大后差不多就是一条链,接下来\(K\)增大就只能删链上的边
然后套路设斜率,现在问题在与如何求删任意条边
设\(dp_{x,0/1}\)为只考虑\(x\)的子树,\(0\)为有一条边到\(x\),\(1\)为最大直径
这个转移有点麻烦,不过大体思路\(0\)是先贪心地连上\(v\in son_x\)所有的直径依附到\(v_1\)上
\(1\)是可以依附到\(v_1,v_2\)或\(v_1\),或者不依附,直接连起来,这个注意只连\(k-1\)次
具体实现先求能贡献的和,在枚举依附的点减取它的贡献即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=4e5+5;
int n;
struct Edge{
int v,val;
};
vector<Edge>g[MAXN];
int x,y,z;
int k;
long long K;
pair<long long,int> Plus(pair<long long,int>ex,pair<long long,int>ey)
{
return make_pair(ex.first+ey.first,ex.second+ey.second);
}
pair<long long,int>dp[MAXN][2];
void dfs(int x,int f)
{
dp[x][0]=make_pair(0,0);
dp[x][1]=make_pair(0,0);
pair<long long,int>Sum=make_pair(0,0);
int Cnt=0;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].v;
int w=g[x][i].val;
if(v==f)
{
continue;
}
dfs(v,x);
pair<long long,int>Con2=Plus(dp[v][1],make_pair(-K,1));
if(Con2.first>=0)
{
Sum=Plus(Sum,Con2);
++Cnt;
}
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].v;
int w=g[x][i].val;
if(v==f)
{
continue;
}
dp[x][1]=max(dp[x][1],dp[v][1]);
}
pair<long long,int>Mxi=make_pair(0,0);
if(Cnt)
{
dp[x][1]=max(dp[x][1],Plus(Sum,make_pair(K,-1)));
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].v;
int w=g[x][i].val;
if(v==f)
{
continue;
}
pair<long long,int>Con1=max(Plus(dp[v][1],make_pair(-K,1)),Plus(dp[v][0],make_pair(w,0)));
pair<long long,int>Con2=Plus(dp[v][1],make_pair(-K,1));
pair<long long,int>aon2=make_pair(-Con2.first,-Con2.second);
pair<long long,int>Sub=Con1;
if(Con2.first>=0)
{
Sub=Plus(Sub,aon2);
}
dp[x][1]=max(dp[x][1],Plus(Sum,Plus(Sub,Mxi)));
Mxi=max(Mxi,Sub);
}
dp[x][0]=Plus(Sum,Mxi);
//printf("%d %lld %lld\n",x,dp[x][0].first,dp[x][1].first);
}
pair<long long,int>Get(long long mid)
{
K=mid;
dfs(1,0);
return dp[1][1];
}
int main()
{
freopen("date.in","r",stdin);
freopen("date.out","w",stdout);
scanf("%d %d",&n,&k);
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&x,&y,&z);
g[x].push_back((Edge){y,z});
g[y].push_back((Edge){x,z});
}
// pair<long long,int>Tx=Get(3);
// printf("%lld %d\n",Tx.first,Tx.second);
long long l=-1e12;
long long r=1e12;
long long Key;
while(l<=r)
{
long long mid=(l+r)>>1ll;
pair<long long,int>Rx=Get(mid);
//printf("%lld %lld %d\n",mid,Rx.first,Rx.second);
if(Rx.second>=k)
{
Key=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
pair<long long,int>Tx=Get(Key);
printf("%lld\n",Tx.first+Key*k);
}
[AGC018E] Sightseeing Plan
首先计算一下每个点到矩形的贡献
这里我们先平移一下,起点为原点
先计算一下\((0,0)\) 到\((x,y)\)为右上角的矩形所有点的方案数
即\(\sum\limits_{i=0}^x\sum\limits_{j=0}^y\binom{i+j}{i}=\sum\limits_{i=0}^x\binom{y+i+1}{i+1}=\sum\limits_{i=1}^{x+1}\binom{y+i}{i}=\binom{x+1+y+1}{x+1}-1\)
这里记\(F(x,y)\)为上式
那到矩形\((a,b)-(c,d)\)的方案就可以据此容斥
即\(F(c,d)-F(a-1,d)-F(c,b-1)+F(a-1,b-1)\)
回到原问题
这里我们可以枚举第二个矩形的进入点\(A\)和出点\(B\),分别计算第一个矩形到\(A\)及\(B\)到第\(3\)个矩形的贡献,\(A\rightarrow B\),还有\(AB\)选个点停留
问题在于这里时间复杂度依旧很大
然后我不会了/kk
设\(C_i\)为\(i=1,2,3\)的矩形
考虑\(A,B\)的贡献是\(F(C_1\rightarrow A)\times F(A\rightarrow B)\times F(B\rightarrow C_2)\times Len_{AB}\)
似乎还是算不了
这里好像可以直接考虑每个边界点的贡献再容斥一下
先把\(Len_{AB}\)拆成\((x_b-x_a)+(y_b-y_a)+1\)
其实如果没有\(Len_{AB}\)的话这里我们可以直接枚举进入点,离开点单独来算贡献的
具体的,我们可以枚举进入矩形的方式
如果拆了\(Len_{AB}\),这里同样可以得到每个点的对应贡献,所以直接独立地算即可
这里离开的同样要算贡献
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=3e6+5;
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int fac[MAXN];
int inv_fac[MAXN];
int C(int n,int m)
{
if(m<0||n<m)
{
return 0;
}
if(n==m||(m==0))
{
return 1;
}
return ((((long long)fac[n]*inv_fac[m])%MOD)*inv_fac[n-m])%MOD;
}
int F(int x,int y)
{
return C(x+y,x);
}
int G(int x,int y)
{
return F((x+1),y+1);
}
int H(int a,int b,int c,int d)
{
return ((long long)G(c,d)-G(c,b-1)-G(a-1,d)+G(a-1,b-1)+2ll*MOD)%MOD;
}
int X[7];
int Y[7];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
fac[0]=1;
for(int i=1;i<=MAXN-5;i++)
{
fac[i]=((long long)fac[i-1]*i)%MOD;
}
inv_fac[MAXN-5]=inv(fac[MAXN-5],MOD);
for(int i=MAXN-5-1;i>=0;i--)
{
inv_fac[i]=((long long)inv_fac[i+1]*(i+1))%MOD;
}
for(int i=1;i<=6;i++)
{
scanf("%d",&X[i]);
}
for(int i=1;i<=6;i++)
{
scanf("%d",&Y[i]);
}
int Res=0;
for(int i=X[3];i<=X[4];i++)
{
int R1=H(i-X[2],(Y[3]-1)-Y[2],i-X[1],(Y[3]-1)-Y[1]);
R1=((long long)R1*H(X[5]-i,Y[5]-Y[3],X[6]-i,Y[6]-Y[3]))%MOD;
R1=((long long)R1*((long long)-i-Y[3]+1+MOD)%MOD)%MOD;
Res=((long long)Res+R1)%MOD;
int R2=H(i-X[2],(Y[4])-Y[2],i-X[1],(Y[4])-Y[1]);
R2=((long long)R2*H(X[5]-i,Y[5]-(Y[4]+1),X[6]-i,Y[6]-(Y[4]+1)))%MOD;
R2=((long long)R2*((long long)i+Y[4])%MOD)%MOD;
Res=((long long)Res+R2)%MOD;
}
for(int i=Y[3];i<=Y[4];i++)
{
int R1=H((X[3]-1)-X[2],i-Y[2],(X[3]-1)-X[1],i-Y[1]);
R1=((long long)R1*H(X[5]-X[3],Y[5]-i,X[6]-X[3],Y[6]-i))%MOD;
R1=((long long)R1*((long long)-i-X[3]+1+MOD)%MOD)%MOD;
Res=((long long)Res+R1)%MOD;
int R2=H((X[4])-X[2],i-Y[2],(X[4])-X[1],i-Y[1]);
R2=((long long)R2*H(X[5]-(X[4]+1),Y[5]-i,X[6]-(X[4]+1),Y[6]-i))%MOD;
R2=((long long)R2*((long long)i+X[4])%MOD)%MOD;
Res=((long long)Res+R2)%MOD;
}
printf("%d\n",Res);
}
某题3
ZDJNB!!!!!
好像要作为月赛题就不放题意了
其实类似于某题\(1\),我们这里计算操作的期望可以只在每个点为链的末端时计算贡献
进一步的,这里我们可以发现每个点作为一个操作末端的概率之和即为答案
而作为末端实际上只与它子树的操作有关
这里我们先删除指定的链并让与链相连的点作为根
可以发现概率就是\(\dfrac{P_x}{Sum_x}\)
\(Sum_x\)是子树内\(P\)的和
考虑维护这个东西
我们定义\(A_i=\dfrac{P_x}{Sum_x}\),根为\(1\)
可以发现如果先考虑\(LCA\)子树内的贡献,实际它就是子树内\(A\)之和减去这条链的\(A\)
而\(LCA\)子树外的点,除了\(1\rightarrow LCA\)这条链,其他点的贡献同样是\(A\)之和
对于单独拎出来的\(1\rightarrow LCA\),对于\(x\rightarrow v\),\(x\)的贡献是\(\dfrac{P_x}{Sum_1-Sum_v}\)
这里可以看成计算边,我们可以维护重链的贡献,轻链与重链的链接现算即可
Show Code
#include<bits/stdc++.h>
#define ls 2*p
#define rs 2*p+1
using namespace std;
const int MAXN=2e5+5;
const int MOD=1e9+7;
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int n,m;
struct Edge{
int u,v;
}edge[MAXN];
vector<int>g[MAXN];
int P[MAXN];
int x,y;
int op;
int dfn[MAXN];
int cnt_dfn;
int top[MAXN];
int Siz[MAXN];
int wson[MAXN];
int dep[MAXN];
int fa[MAXN];
int Sum[MAXN];
int Ps[MAXN];
int Rlf[MAXN];
void dfs1(int x,int f)
{
Siz[x]=1;
Sum[x]=P[x];
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
fa[v]=x;
dep[v]=dep[x]+1;
dfs1(v,x);
Sum[x]=((long long)Sum[x]+Sum[v])%MOD;
Ps[x]=((long long)Ps[x]+Ps[v])%MOD;
Siz[x]+=Siz[v];
if(Siz[wson[x]]<Siz[v])
{
wson[x]=v;
}
}
Ps[x]=((long long)Ps[x]+((long long)P[x]*inv(Sum[x],MOD))%MOD)%MOD;
}
void dfs2(int x,int Top)
{
top[x]=Top;
dfn[x]=++cnt_dfn;
Rlf[cnt_dfn]=x;
if(!wson[x])
{
return;
}
dfs2(wson[x],Top);
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==fa[x])
{
continue;
}
if(v==wson[x])
{
continue;
}
dfs2(v,v);
}
}
struct Seg{
int l,r;
int dt1;
int dt2;
}Tree[MAXN*4];
void push_up(int p)
{
Tree[p].dt1=((long long)Tree[ls].dt1+Tree[rs].dt1)%MOD;
Tree[p].dt2=((long long)Tree[ls].dt2+Tree[rs].dt2)%MOD;
}
void Build(int p,int l,int r)
{
Tree[p].l=l;
Tree[p].r=r;
if(l==r)
{
Tree[p].dt1=((long long)P[Rlf[l]]*inv((Sum[Rlf[l]]),MOD))%MOD;
Tree[p].dt2=((long long)P[Rlf[l]]*inv(((long long)Sum[1]-Sum[Rlf[l+1]]+MOD)%MOD,MOD))%MOD;
return;
}
int mid=(l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
push_up(p);
}
void Update2(int p,int k,int x)
{
//printf("rnmrnrmrmrnrrmmrmrmrmrmrmrmr\n");
if(Tree[p].l==Tree[p].r)
{
Tree[p].dt2=x;
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(k<=mid)
{
Update2(ls,k,x);
}
else
{
Update2(rs,k,x);
}
push_up(p);
}
int Query2(int p,int l,int r)
{
if(l>r)
{
return 0;
}
if(Tree[p].l>=l&&Tree[p].r<=r)
{
return Tree[p].dt2;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
int Res=0;
if(l<=mid)
{
Res=((long long)Res+Query2(ls,l,r))%MOD;
}
if(r>mid)
{
Res=((long long)Res+Query2(rs,l,r))%MOD;
}
return Res;
}
void Update1(int p,int k,int x)
{
if(Tree[p].l==Tree[p].r)
{
Tree[p].dt1=x;
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(k<=mid)
{
Update1(ls,k,x);
}
else
{
Update1(rs,k,x);
}
push_up(p);
}
int Query1(int p,int l,int r)
{
if(Tree[p].l>=l&&Tree[p].r<=r)
{
return Tree[p].dt1;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
int Res=0;
if(l<=mid)
{
Res=((long long)Res+Query1(ls,l,r))%MOD;
}
if(r>mid)
{
Res=((long long)Res+Query1(rs,l,r))%MOD;
}
return Res;
}
int Query_lca(int x,int y)
{
while((top[x]!=top[y]))
{
if(dep[top[x]]<dep[top[y]])
{
swap(x,y);
}
x=fa[top[x]];
}
if(dep[x]<dep[y])
{
swap(x,y);
}
return y;
}
int Query_chain1(int x,int y)
{
if(!y)
{
return 0;
}
int Rp=0;
while((top[x]!=top[y]))
{
if(dep[top[x]]<dep[top[y]])
{
swap(x,y);
}
Rp=((long long)Rp+Query1(1,dfn[top[x]],dfn[x]))%MOD;
x=fa[top[x]];
}
if(dep[x]<dep[y])
{
swap(x,y);
}
Rp=((long long)Rp+Query1(1,dfn[y],dfn[x]))%MOD;
return Rp;
}
int Query_chain2(int x,int y)
{
int Rp=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
{
swap(x,y);
}
Rp=((long long)Rp+Query2(1,dfn[top[x]],dfn[x]-1))%MOD;
x=top[x];
Rp=((long long)Rp+((long long)P[fa[x]]*inv(((long long)Sum[1]-Sum[x]+MOD)%MOD,MOD))%MOD)%MOD;
x=fa[x];
}
if(dep[x]<dep[y])
{
swap(x,y);
}
Rp=((long long)Rp+Query2(1,dfn[y],dfn[x]-1))%MOD;
return Rp;
}
signed main()
{
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&P[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
edge[i].u=x;
edge[i].v=y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
Build(1,1,n);
while(m--)
{
scanf("%d",&op);
if(op==1)
{
int k;
scanf("%d",&k);
x=edge[k].u;
y=edge[k].v;
if(dep[x]>dep[y])
{
swap(x,y);
}
Sum[y]=((long long)Sum[y]-P[y]+P[x]+MOD)%MOD;
swap(P[x],P[y]);
Update2(1,dfn[x],((long long)P[x]*inv(((long long)Sum[1]-Sum[wson[x]]+MOD)%MOD,MOD)%MOD));
Update2(1,dfn[y],((long long)P[y]*inv(((long long)Sum[1]-Sum[wson[y]]+MOD)%MOD,MOD)%MOD));
Update1(1,dfn[x],((long long)P[x]*inv(Sum[x],MOD))%MOD);
Update1(1,dfn[y],((long long)P[y]*inv(Sum[y],MOD))%MOD);
}
else
{
scanf("%d %d",&x,&y);
int Tr=Query1(1,1,n);
Tr=((long long)Tr-Query_chain1(x,y)+MOD)%MOD;
int LCA=(Query_lca(x,y));
Tr=((long long)Tr-Query_chain1(1,fa[LCA])+MOD)%MOD;
Tr=((long long)Tr+Query_chain2(1,LCA))%MOD;
printf("%d\n",Tr);
}
}
}
[ARC098F] Donation
一个有趣的题
首先我们先得明确一个点如果捐了就不会再经过它
考虑如果捐了又经过它,那我如果是第二次捐我走的这路径的余钱明显更多
很明显这里捐钱就相当于删点
这里我们考虑倒着做这个过程,令\(C_x=max(0,A_x-B_x)\),那我们到达一个点必须先有\(C_x\),并且第一次到达可以抢\(B_x\)的钱
很明显这里先走\(C_x\)小的更优
如果我们让边权为\(max(C_x,C_y)\),那这个问题就类似于瓶颈路
考虑\(kruskal\)重构树,很明显走如果能走\(u\),就相当于走完整个连通块
设\(dp_x\)为从\(x\)某点出发走完整个联通块最小初始钱数
对于\(v\),走出来的钱数为\(dp_v+Sumb_v\),那它需要加的钱数为\(max(0,C_u-(dp_v+Sumb_v))\),最后取\(min\)即可
Show Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=2e5+5;
int n;
int m;
int A[MAXN];
int B[MAXN];
int x,y;
int C[MAXN];
struct Edge{
int u,v,val;
bool operator<(const Edge x)const{
return val<x.val;
}
}edge[MAXN*2];
int fa[MAXN*2];
int find(int x)
{
if(fa[x]==x)
{
return x;
}
fa[x]=find(fa[x]);
return fa[x];
}
int Val[MAXN*2];
vector<int>g[MAXN*2];
int dp[MAXN*2];
void dfs(int x)
{
if(x<=n)
{
dp[x]=Val[x];
return;
}
dp[x]=1e18;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
dfs(v);
B[x]+=B[v];
int Tpx=dp[v]+B[v];
dp[x]=min(dp[x],dp[v]+max(0ll,Val[x]-Tpx));
}
}
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld %lld",&A[i],&B[i]);
C[i]=max(0ll,A[i]-B[i]);
fa[i]=i;
Val[i]=C[i];
}
for(int i=1;i<=m;i++)
{
scanf("%lld %lld",&x,&y);
edge[i].u=x;
edge[i].v=y;
edge[i].val=max(C[x],C[y]);
}
sort(edge+1,edge+1+m);
int cnt_node=n;
for(int i=1;i<=m;i++)
{
int ex=find(edge[i].u);
int ey=find(edge[i].v);
if(ex!=ey)
{
++cnt_node;
Val[cnt_node]=edge[i].val;
fa[cnt_node]=cnt_node;
fa[ex]=cnt_node;
fa[ey]=cnt_node;
g[cnt_node].push_back(ex);
g[cnt_node].push_back(ey);
}
}
//printf("%d???\n",cnt_node);
dfs(cnt_node);
printf("%lld\n",dp[cnt_node]+B[cnt_node]);
}
CF1783G Weighed Tree Radius
一个有趣的题
首先如果没有加权,考虑半径是什么
其实就是直径的中点,因为中点时半径\(R=\lceil\dfrac{d}{2}\rceil\),可以发现其余点的偏心距是一定大于这个的
如果加权,我们可以在每个点上挂两个点\(A_i\)
如果我们定义直径为\(dis(u,v)+A_u+A_v\),可以发现同样这样能覆盖所有的情况
而在这种情况下同样\(mid\)是圆心
于是我们只需动态维护直径即可
Show Code
#include<bits/stdc++.h>
#define ls 2*p
#define rs 2*p+1
using namespace std;
const int MAXN=2e5+5;
int n;
int q;
vector<int>g[MAXN];
int x,y;
int Siz[MAXN];
int Rlf[MAXN];
int wson[MAXN];
int top[MAXN];
int fa[MAXN];
int dep[MAXN];
int cnt_dfn;
int dfn[MAXN];
int A[MAXN];
void dfs1(int x,int f)
{
Siz[x]=1;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
long long w=g[x][i];
if(v==f)
{
continue;
}
dep[v]=dep[x]+1;
fa[v]=x;
dfs1(v,x);
Siz[x]+=Siz[v];
if(Siz[v]>Siz[wson[x]])
{
wson[x]=v;
}
}
}
void dfs2(int x,int Top)
{
dfn[x]=++cnt_dfn;
Rlf[cnt_dfn]=x;
top[x]=Top;
if(!wson[x])
{
return;
}
dfs2(wson[x],Top);
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==wson[x])
{
continue;
}
if(v==fa[x])
{
continue;
}
dfs2(v,v);
}
}
int LCA(int x,int y)
{
while((top[x]!=top[y]))
{
if(dep[top[x]]<dep[top[y]])
{
swap(x,y);
}
x=fa[top[x]];
}
if(dep[x]<dep[y]){
swap(x,y);
}
return y;
}
int dist(int x,int y)
{
return dep[x]+dep[y]-2*dep[LCA(x,y)]+A[x]+A[y];
}
struct Seg{
pair<int,int>date;
int l,r;
}Tree[MAXN*4];
void push_up(int p)
{
int Maxi=-1;
if(dist(Tree[ls].date.first,Tree[ls].date.second)>Maxi)
{
Maxi=dist(Tree[ls].date.first,Tree[ls].date.second);
Tree[p].date.first=Tree[ls].date.first;
Tree[p].date.second=Tree[ls].date.second;
}
if(dist(Tree[ls].date.first,Tree[rs].date.first)>Maxi)
{
Maxi=dist(Tree[ls].date.first,Tree[rs].date.first);
Tree[p].date.first=Tree[ls].date.first;
Tree[p].date.second=Tree[rs].date.first;
}
if(dist(Tree[ls].date.first,Tree[rs].date.second)>Maxi)
{
Maxi=dist(Tree[ls].date.first,Tree[rs].date.second);
Tree[p].date.first=Tree[ls].date.first;
Tree[p].date.second=Tree[rs].date.second;
}
if(dist(Tree[ls].date.second,Tree[rs].date.first)>Maxi)
{
Maxi=dist(Tree[ls].date.second,Tree[rs].date.first);
Tree[p].date.first=Tree[ls].date.second;
Tree[p].date.second=Tree[rs].date.first;
}
if(dist(Tree[ls].date.second,Tree[rs].date.second)>Maxi)
{
Maxi=dist(Tree[ls].date.second,Tree[rs].date.second);
Tree[p].date.first=Tree[ls].date.second;
Tree[p].date.second=Tree[rs].date.second;
}
if(dist(Tree[rs].date.first,Tree[rs].date.second)>Maxi)
{
Maxi=dist(Tree[rs].date.first,Tree[rs].date.second);
Tree[p].date.first=Tree[rs].date.first;
Tree[p].date.second=Tree[rs].date.second;
}
return;
}
void Build(int p,int l,int r)
{
Tree[p].l=l;
Tree[p].r=r;
if(l==r)
{
Tree[p].date.first=Rlf[l];
Tree[p].date.second=Rlf[l];
return;
}
int mid=(l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
push_up(p);
}
void Update(int p,int k)
{
if(Tree[p].l==Tree[p].r)
{
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(k<=mid)
{
Update(ls,k);
}
else
{
Update(rs,k);
}
push_up(p);
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&A[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
Build(1,1,n);
scanf("%d",&q);
while(q--)
{
scanf("%d %d",&x,&y);
A[x]=y;
Update(1,dfn[x]);
printf("%d\n",(dist(Tree[1].date.first,Tree[1].date.second)+1)/2);
}
}
CF566C Logistical Questions
如果没有\(\dfrac{3}{2}\)的次方,这玩意就是树的带权重心
对于\(f_x=\sum\limits_{i=1}^n(dis(i,x)^\frac{3}{2}w_i)\)
可以发现如果我们固定\(x\),按照树上顺序\(dis(i,x)^{\frac{3}{2}}\)是下凸的
那么对于所有的\(i\)求和按照树上的顺序\(f_x\)同样是下凸的
并且由于有根号操作,事实上不会出现平台的情况
更好理解的话同样是不管这个\(\dfrac{3}{2}\)次方,其中一个重心为根往下\(f_x\)单增,事实上这个也是同理的
同时可以发现对于\(x\),只有一个儿子\(v\)的子树可能比\(x\)作为重心优,因为如果有两个明显不满足下凸性质
这其实我们对于\(x\)找可能成为答案节点所在点子树\(v\)
这里直接维护增量似乎不好做
但对于\(dis(x,u)^{\frac{3}{2}}w_i\),如果对其求导,计算出子树\(v\)对应项导数之和\(p_v\),我们就可以根据\(\sum\limits_{v\in son_x}p_v-2\times p_v\)是否小于\(0\)来判断\(v\rightarrow x\)是否有\(f\)减小的趋势
注意这里不一定\(v\)的子树更优,但向\((x,v)\)这条边走一定是更优的
最后只需要用个点分治类似的东西就可以了
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+5;
struct Edge{
int v,val;
}edge[MAXN];
int n;
vector<Edge>g[MAXN];
int A[MAXN];
int x,y,z;
int Siz[MAXN];
int Sums=0;
int Minh=0x3f3f3f3f;
int Heart=0;
int Vis[MAXN];
void Find_Heart(int x,int f)
{
Siz[x]=1;
int Maxs=0;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].v;
if(v==f)
{
continue;
}
if(Vis[v])
{
continue;
}
Find_Heart(v,x);
Siz[x]+=Siz[v];
Maxs=max(Maxs,Siz[v]);
}
Maxs=max(Maxs,Sums-Siz[x]);
if(Maxs<Minh)
{
Minh=Maxs;
Heart=x;
}
}
double Res;
int Center;
double Sum;
double P[MAXN];
int Key;
int dis[MAXN];
void dfs(int x,int f)
{
Sum+=(A[x]*pow(dis[x],1.5));
P[Key]+=(A[x]*pow(dis[x],0.5));
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].v;
int w=g[x][i].val;
if(v==f)
{
continue;
}
dis[v]=dis[x]+w;
dfs(v,x);
}
}
void solve(int x)
{
Vis[x]=1;
Find_Heart(x,0);
double Sp=0;
Sum=0;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].v;
int w=g[x][i].val;
Key=v;
dis[v]=w;
P[v]=0;
dfs(v,x);
Sp+=P[v];
}
//cerr<<"fuck"<<endl;
//cerr<<x<<endl;
if(Sum<Res||(!Center))
{
Res=Sum;
Center=x;
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].v;
if(Vis[v])
{
continue;
}
if(Sp-2*P[v]<0)
{
Sums=Siz[v];
Minh=0x3f3f3f3f;
Find_Heart(v,x);
solve(Heart);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&A[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&x,&y,&z);
g[x].push_back((Edge){y,z});
g[y].push_back((Edge){x,z});
}
Sums=n;
Minh=0x3f3f3f3f;
Find_Heart(1,0);
solve(Heart);
printf("%d %.9lf\n",Center,Res);
}
[CTSC2017] 网络
当草稿了
Show Code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
namespace Read {
static const int buf_size = 1 << 12;
static const bool use_fread = true;
static unsigned char buf[buf_size];
static int buf_len, buf_pos;
bool isEOF() {
if (buf_pos == buf_len) {
buf_pos = 0; buf_len = fread(buf, 1, buf_size, stdin);
if (buf_pos == buf_len) return true;
}
return false;
}
char readChar() {
if (!use_fread) return getchar();
return isEOF() ? EOF : buf[buf_pos++];
}
LL rint() {
LL x = 0, Fx = 1; char c = readChar();
while (c < '0' || c > '9') { Fx ^= (c == '-'); c = readChar(); }
while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = readChar(); }
return Fx ? x : -x;
}
template <typename T>
void read(T &x) {
x = rint();
}
template <typename T, typename... Ts>
void read(T &x, Ts &...rest) {
read(x);
read(rest...);
}
} using namespace Read;
const int MAXN=1e5+5;
int n;
int L;
struct Edge{
int nex,v,val;
}edge[MAXN*2];
int cnt_edge;
int head[MAXN];
void Add(int u,int v,int val)
{
++cnt_edge;
edge[cnt_edge].nex=head[u];
edge[cnt_edge].v=v;
edge[cnt_edge].val=val;
head[u]=cnt_edge;
return;
}
int x,y,z;
int cnt_l;
int Loap[MAXN];
long long D[MAXN];
long long Li[MAXN];
long long Dis[MAXN];
int fa[MAXN];
int fv[MAXN];
bool Vis[MAXN];
int Key;
long long Md=-1;
void dfs1(int x,int f)
{
if(Dis[x]>Md)
{
Md=Dis[x];
Key=x;
}
fa[x]=f;
for(int i=head[x];i;i=edge[i].nex)
{
int v=edge[i].v;
int w=edge[i].val;
if(v==f)
{
continue;
}
Dis[v]=Dis[x]+w;
fv[v]=w;
dfs1(v,x);
}
}
long long dm[MAXN];
long long l;
void dfs2(int x,int f)
{
for(int i=head[x];i;i=edge[i].nex)
{
int v=edge[i].v;
int w=edge[i].val;
if(v==f)
{
continue;
}
if(Vis[v])
{
continue;
}
dfs2(v,x);
dm[x]=max(dm[x],dm[v]+w);
}
if(f)
{
long long Mxi=0;
long long Sec=0;
for(int i=head[x];i;i=edge[i].nex)
{
int v=edge[i].v;
int w=edge[i].val;
if(v==f)
{
continue;
}
if(Vis[v])
{
continue;
}
if(dm[v]>=Mxi)
{
Sec=Mxi;
Mxi=dm[v];
}
else if(dm[v]>Sec)
{
Sec=dm[v];
}
}
l=max(l,Mxi+Sec);
}
}
int cnt_lsh=0;
long long Bit[MAXN*2];
int lowbit(int x){
return x&(-x);
}
void update(int k,long long x)
{
for(int i=k;i>=1;i-=lowbit(i))
{
Bit[i]=max(Bit[i],x);
}
}
long long Query(int k)
{
long long Res=-1e15;
for(int i=k;i<=cnt_lsh;i+=lowbit(i))
{
Res=max(Res,Bit[i]);
}
return Res;
}
long long lsh[MAXN*2];
int Tp[MAXN],Tq[MAXN];
bool check(long long mid)
{
long long R2=-1e11;
long long R1=-1e11;
cnt_lsh=0;
for(int i=1;i<=cnt_l;i++)
{
lsh[++cnt_lsh]=mid-(D[i]+Li[i]);
lsh[++cnt_lsh]=(-Li[i]+D[i]);
}
sort(lsh+1,lsh+1+cnt_lsh);
cnt_lsh=unique(lsh+1,lsh+1+cnt_lsh)-lsh-1;
for(int i=1;i<=cnt_lsh;i++)
{
Bit[i]=-1e15;
}
for(int i=1;i<=cnt_l;i++)
{
Tp[i]=lower_bound(lsh+1,lsh+1+cnt_lsh,mid-(D[i]+Li[i]))-lsh;
Tq[i]=lower_bound(lsh+1,lsh+1+cnt_lsh,-Li[i]+D[i])-lsh;
R1=max(R1,Query(Tp[i]+1)-mid+L+D[i]-Li[i]);
R2=max(R2,Query(Tp[i]+1)-mid+L+D[i]+Li[i]);
update(Tq[i],-Li[i]+D[i]);
}
for(int i=1;i<=cnt_lsh;i++)
{
Bit[i]=-1e15;
}
if(R1>R2)
{
return 0;
}
long long R4=-1e11;
long long R3=-1e11;
for(int i=1;i<=cnt_l;i++)
{
R3=max(R3,Query(Tp[i]+1)-mid+L+D[i]-Li[i]);
R4=max(R4,Query(Tp[i]+1)-mid+L+D[i]+Li[i]);
update(Tq[i],(Li[i]+D[i]));
}
if(R3>R4)
{
return 0;
}
int pa1=cnt_l,pa2=0,pa3=1,pa4=cnt_l+1;
for(int i=1;i<=cnt_l;i++)
{
while(pa1&&R1>(-Li[pa1]-Li[i]))
{
pa1--;//[1,pa1]
}
while(pa2+1<=cnt_l&&R2<=(-Li[pa2+1]+Li[i]))
{
pa2++;//[1,pa2]
}
while(pa3<=n&&R3>(Li[pa3]-Li[i]))
{
pa3++;//[pa3,n]
}
while(pa4-1>=1&&R4<=(Li[pa4-1]+Li[i]))
{
pa4--;//[pa4,n]
}
if(min(pa1,pa2)>=max(pa3,pa4))
{
return 1;
}
}
return 0;
}
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
while(1)
{
read(n,L);
if(n==0)
{
break;
}
cnt_edge=0;
for(int i=1;i<=n;i++)
{
head[i]=0;
Vis[i]=0;
Dis[i]=0;
dm[i]=0;
}
for(int i=1;i<n;i++)
{
read(x,y,z);
Add(x,y,z);
Add(y,x,z);
}
Dis[1]=0;
Md=-1;
dfs1(1,0);
int Kex=Key;
Dis[Kex]=0;
Md=-1;
dfs1(Kex,0);
int Now=Key;
int Lv=0;
cnt_l=0;
l=0;
// printf("%lld???\n",fv[5]);
while(Now)
{
Loap[++cnt_l]=Now;
Li[cnt_l]=Li[cnt_l-1]+Lv;
Lv=fv[Now];
Now=fa[Now];
}
for(int i=1;i<=cnt_l;i++)
{
Vis[Loap[i]]=1;
}
for(int i=1;i<=cnt_l;i++)
{
dfs2(Loap[i],0);
D[i]=dm[Loap[i]];
// printf("%d %d??\n",Loap[i],D[i]);
}
long long Key=-1;
long long r=Li[cnt_l];
while(l<=r)
{
long long mid=(l+r)>>1;
if(check(mid))
{
r=mid-1;
Key=mid;
}
else
{
l=mid+1;
}
}
printf("%lld\n",Key);
}
}
「CEOI2020」象棋世界
究极恶心的五合一
P,Q,R
比较简单,注意\(Q\)的方案数可能有多种,这里最好是手动枚举每种情况
B
一个不难想到的想法,我们每次走到边界就反弹
但这样走实际上很难保证走到\(R\)时同时也走到\(C_r\)
这里如果考虑每个拐点向内收缩,每收缩一次对于同样的列行会\(-2\)
这启示我们只需要保证终点是\(x\ge R,y=C_r\)即可
同时我们要收缩的次数为\(\dfrac{x-R}{2}\),注意到最多为\(C\),很明显每个拐点都能收缩任意次
最后的方案就是分配\(\dfrac{x-R}{2}\)到\(t\)个拐点上,经典的隔板法,最后是\(\binom{t+\frac{x-R}{2}-1}{t}\)
注意\(t\)的范围可能很大,这里直接暴力算,因为\(\dfrac{x-R}{2}\le C\)
K
这个问题类似于\(ABC309Ex\)
我们不难发现第一个答案就是\(R-1\)
直接设\(F_i(x)=\sum\limits_{j=0}f_{i,j}x^j\),其中\(f_{i,j}\)表示固定起点为\((1,C_1)\)时走到\((i,j)\)的方案数
考虑如果没有边界,其实\(F_i(x)=F_{i-1}(x)(x+1+x^{-1})\)
问题出在\(x^0,x^{m+1}\)应该始终保持为\(0\)
考虑以\(m+1\)为对称轴对称一份\(F_i(x)\)且对应位置值相反
可以发现这时我们如果\(\times(x+1+x^{-1})\),\(x^{(m+1)}\)的系数始终为\(0\),因为会被抵消这是怎么想到的
同样的思路,如果我们直接做循环卷积,\(x^0\)的值同样会抵消
同时,因为循环卷积同样有结合律
所以直接循环卷积的快速幂即可,具体的就是\([x^{Cr}](x+1+x^{-1})^{R-1}(x^{C_1}-x^{2m+2-C_1})\)
\(x^{-1}\)我们这里还是先乘上\(x\),最后减一下就行了,最后询问的时候其实直接算两个位置就可以了,有点卡,\(1e9+7\)要用任意模数
Show Code
#include<bits/stdc++.h>
using namespace std;
int Abs(int x)
{
return x>0?x:-x;
}
const int MAXN=2000+5;
const int MOD=1e9+7;
const long double Pi=acos(-1.0);
const int p=32000;
int Rev[MAXN*4];
struct Cpx{
long double a,b;
Cpx(){
a=0;
b=0;
}
Cpx(long double aa,long double bb){
a=aa;
b=bb;
}
Cpx operator*(const Cpx x)const{
return Cpx(a*x.a-b*x.b,b*x.a+a*x.b);
}
Cpx operator+(const Cpx x)const{
return Cpx(a+x.a,b+x.b);
}
Cpx operator-(const Cpx x)const{
return Cpx(a-x.a,b-x.b);
}
};
int Pow(int a,int b,int pff){
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%pff;
}
base=((long long)base*base)%pff;
b>>=1;
}
return res;
}
int inv(int a,int pff){
return Pow(a,pff-2,pff);
}
struct Poly{
vector<int>U;
vector<Cpx>V;
int size()
{
return U.size();
}
void push_back(int x)
{
U.push_back(x);
return;
}
void clear()
{
U.clear();
return;
}
void FFT(int Limit,int type)
{
int Len=(1<<Limit);
for(int i=0;i<Len;i++)
{
Rev[i]=((Rev[i>>1]>>1)|((i&1)<<(Limit-1)));
}
while(V.size()<Len)
{
V.push_back(Cpx(0,0));
}
for(int i=0;i<Len;i++)
{
if(i<Rev[i])
{
swap(V[i],V[Rev[i]]);
}
}
for(int l=1;l<Len;l<<=1)
{
Cpx Wn=Cpx(cos(Pi/l),type*sin(Pi/l));
for(int i=0;i<Len;i+=(l<<1))
{
Cpx W=Cpx(1,0);
for(int j=i;j<i+l;j++,W=W*Wn)
{
Cpx Xc=V[j];
Cpx Yc=V[j+l]*W;
V[j]=(Xc+Yc);
V[j+l]=(Xc-Yc);
}
}
}
if(type==-1)
{
for(int i=0;i<Len;i++)
{
V[i].a/=Len;
}
}
}
}A,B,Cd;
Poly Mul_FFT(Poly A,Poly B){
int N=A.V.size();
int M=B.V.size();
int nox=1;
int Lm=0;
while(nox<=(N+M-2))
{
nox<<=1;
Lm++;
}
A.FFT(Lm,1);
B.FFT(Lm,1);
for(int i=0;i<nox;i++)
{
A.V[i]=A.V[i]*B.V[i];
}
A.FFT(Lm,-1);
while(A.V.size()>(N+M-1))
{
A.V.pop_back();
}
return A;
}
Poly Mul_MTT(Poly A,Poly B,int P){
int n=A.U.size()-1;
int m=B.U.size()-1;
Poly A1,B1,C1,D1;
A1.V.clear();
B1.V.clear();
C1.V.clear();
D1.V.clear();
Poly A2,B2,C2,D2;
A2.V.clear();
B2.V.clear();
C2.V.clear();
D2.V.clear();
for(int i=0;i<=n;i++)
{
int x=A.U[i];
int ax=x/p;
int bx=x%p;
A1.V.push_back(Cpx(ax,0));
B1.V.push_back(Cpx(ax,0));
C1.V.push_back(Cpx(bx,0));
D1.V.push_back(Cpx(bx,0));
}
for(int i=0;i<=m;i++)
{
int x=B.U[i];
int ax=x/p;
int bx=x%p;
A2.V.push_back(Cpx(ax,0));
B2.V.push_back(Cpx(bx,0));
C2.V.push_back(Cpx(ax,0));
D2.V.push_back(Cpx(bx,0));
}
A1=Mul_FFT(A1,A2);
B1=Mul_FFT(B1,B2);
C1=Mul_FFT(C1,C2);
D1=Mul_FFT(D1,D2);
Poly Polat;
Polat.U.clear();
for(int i=0;i<A1.V.size();i++)
{
long long F1=(long long)(A1.V[i].a+0.5);
long long F2=(long long)(B1.V[i].a+0.5);
long long F3=(long long)(C1.V[i].a+0.5);
long long F4=(long long)(D1.V[i].a+0.5);
long long F=(((F1*p)%P*p)%P+(((F2+F3)%P)*p)%P+F4)%P;
Polat.U.push_back(F);
}
return Polat;
}
Poly Mul_Mod(Poly A,Poly B,int M)
{
Poly C=Mul_MTT(A,B,MOD);
Poly D;
D.U.resize(M,0);
for(int i=0;i<C.size();i++)
{
D.U[i%M]=((long long)D.U[i%M]+C.U[i])%MOD;
}
return D;
}
int R,Cp;
int q;
char s[105];
int C1,Cr;
int C(int n,int m)
{
int res=1;
for(int i=n-m+1;i<=n;i++)
{
res=((long long)res*i)%MOD;
}
int Mul=1;
for(int i=1;i<=m;i++)
{
Mul=((long long)Mul*i)%MOD;
}
res=((long long)res*inv(Mul,MOD))%MOD;
return res;
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d %d",&R,&Cp,&q);
B.U.resize(3,0);
B.U[0]=1;
B.U[1]=1;
B.U[2]=1;
Cd.U.resize(1,0);
Cd.U[0]=1;
int n=R-1;
int m=Cp;
while(n)
{
if(n&1)
{
Cd=Mul_Mod(Cd,B,(2*(m+1)));
}
B=Mul_Mod(B,B,(2*(m+1)));
n>>=1;
}
int M=(2*(m+1));
while(q--)
{
scanf("%s",s);
scanf("%d %d",&C1,&Cr);
if(s[0]=='P')
{
if(C1!=Cr)
{
printf("%d %d\n",0,0);
}
else
{
printf("%d %d\n",R-1,1);
}
}
else if(s[0]=='R')
{
if(C1==Cr)
{
printf("1 1\n");
}
else
{
printf("2 2\n");
}
}
else if(s[0]=='Q')
{
if(C1==Cr)
{
printf("1 1\n");
}
else if((R-1==Abs(C1-Cr)))
{
printf("1 1\n");
}
else
{
printf("2 ");
int Tot=0;
if(C1>Cr)
{
++Tot;
}
if((Cr-(R-1))<C1&&(Cr-(R-1))>0)
{
++Tot;
}
++Tot;
++Tot;
if(C1<Cr)
{
++Tot;
}
if(Cr+(R-1)>C1&&Cr+(R-1)<=Cp)
{
++Tot;
}
if(Cr<C1)
{
if(C1-(R-1)>0)
{
++Tot;
}
if(C1-(R-1)<Cr)
{
++Tot;
}
if(((R+1-Cr+C1)%2==0)&&((R+1-Cr+C1)/2>0)&&((R+1-Cr+C1)/2<=R)&&(C1+Cr-R+1)/2>0&&(C1+Cr-R+1)/2<=Cp)
{
++Tot;
}
}
else
{
if(C1-(R-1)>0)
{
++Tot;
}
if(((R+1-Cr+C1)%2==0)&&((R+1-Cr+C1)/2>0)&&((R+1-Cr+C1)/2<=R)&&(C1+Cr-R+1)/2>0&&(C1+Cr-R+1)/2<=Cp)
{
++Tot;
}
}
if(Cr>C1)
{
if(C1+(R-1)<=Cp)
{
++Tot;
}
if(C1+(R-1)>Cr)
{
++Tot;
}
if(((R+1+Cr-C1)%2==0)&&((R+1+Cr-C1)/2>0)&&((R+1+Cr-C1)/2<R)&&(C1+Cr+R-1)/2>0&&(C1+Cr+R-1)/2<=Cp)
{
++Tot;
}
}
else
{
if(C1+(R-1)<=Cp)
{
++Tot;
}
if(((R+1+Cr-C1)%2==0)&&((R+1+Cr-C1)/2>0)&&((R+1+Cr-C1)/2<R)&&(C1+Cr+R-1)/2>0&&(C1+Cr+R-1)/2<=Cp)
{
++Tot;
}
}
printf("%d\n",Tot);
}
}
else if(s[0]=='B')
{
if(((C1+1)&1)!=((Cr+R)&1))
{
printf("0 0\n");
}
else if((R-1)==Abs(C1-Cr))
{
printf("1 1\n");
}
else
{
int T=0;
int A;
int Tx=(C1-1+1);
int Rx=(R-Tx);
T++;
T+=(Rx/(Cp-1));
Tx+=((Rx/(Cp-1)))*(Cp-1);
if((Rx/(Cp-1))&1)
{
int Rpx=(R-Tx);
if(Cr>(Cp-Rpx))
{
T+=2;
Tx+=(Cp-1);
A=Tx+(Cr-1);
}
else
{
++T;
A=R+((Cp-Rpx)-Cr);
}
if(R==Tx&&(Cp==Cr))
{
T--;
}
}
else
{
int Rpx=(R-Tx);
if(Cr<Rpx+1)
{
T+=2;
Tx+=(Cp-1);
A=Tx+(Cp-Cr);
}
else
{
++T;
A=R+(Cr-(Rpx+1));
}
if(R==Tx&&(Rpx+1==Cr))
{
T--;
}
}
int Mt=T;
int Ma=A;
T=0;
Tx=(1+(Cp-C1));
Rx=(R-Tx);
T++;
T+=(Rx/(Cp-1));
Tx+=((Rx/(Cp-1)))*(Cp-1);
if((Rx/(Cp-1))%2==0)
{
int Rpx=(R-Tx);
if(Cr>(Cp-Rpx))
{
T+=2;
Tx+=(Cp-1);
A=Tx+(Cr-1);
}
else
{
++T;
A=R+((Cp-Rpx)-Cr);
}
if(R==Tx&&(Cp==Cr))
{
T--;
}
}
else
{
int Rpx=(R-Tx);
if(Cr<Rpx+1)
{
T+=2;
Tx+=(Cp-1);
A=Tx+(Cp-Cr);
}
else
{
++T;
A=R+(Cr-(Rpx+1));
}
if(R==Tx&&(Rpx+1==Cr))
{
T--;
}
}
if(T<Mt)
{
printf("%d ",T);
int Tpv=(A-R)/2;
printf("%d\n",C((T-1)+Tpv-1,Tpv));
}
else if(T>Mt)
{
printf("%d ",Mt);
int Tpv=(Ma-R)/2;
printf("%d\n",C((Mt-1)+Tpv-1,Tpv));
}
else if(T==Mt)
{
printf("%d ",Mt);
int Tpv=(Ma-R)/2;
int Tpq=(A-R)/2;
int Ans=C((Mt-1)+Tpv-1,Tpv);
Ans=((long long)Ans+C((T-1)+Tpq-1,Tpq))%MOD;
printf("%d\n",Ans);
}
}
}
else if(s[0]=='K')
{
printf("%d ",R-1);
A.U.clear();
A.U.resize(2*m+2,0);
A.U[C1]++;
A.U[(2*(m+1))-C1]=((long long)A.U[(2*(m+1))-C1]-1+MOD)%MOD;
//A=Mul_Mod(A,Cd,(2*(m+1)));
int Res=0;
int Key=(Cr+(R-1))%(2*(m+1));
Res=Cd.U[(Key-C1+M)%M];
Res=((long long)Res-Cd.U[(Key-((2*(m+1))-C1)+M)%M]+MOD)%MOD;
printf("%d\n",Res);
}
}
}