把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P8290 [省选联考 2022] 填树

题面传送门

写完这题发现自己不是很会拉格朗日插值,考场上肯定药丸。

首先看第一问,我们有一个非常显然的想法:枚举最小值是哪个,然后每个点可以求出一个取值区间,这样可以树上合并路径的dp做到单次\(O(n)\)

但是这种想法有一个问题:即不能保证最小值一定是我枚举的这个,换言之,一种方案可能在不同的我所枚举的最小值中被计算很多次。

考虑容斥,记我们枚举的答案为\(f(l,l+k)\),容易发现我们要最小值一定为\(l\)只需要减去\(f(l+1,l+k)\)即可。于是我们可以过四个点。

发现这个东西只是\(n\)个值乘起来。也就是说一条路径其实是一个\(n\)次多项式,加上枚举两端点的两次和前缀和的一次,总共是\(n+3\)次,我们就可以愉快的插值了。总共有\(O(n)\)段本质不同的区间,于是总复杂度\(O(n^3)\)

再考虑第二问,发现前面我们所干的事情可以原封不动保留,然后在dp时稍微修改一下即可。这里每个点变成两次所以次数是\(n+4\)次。也可以插值。

总复杂度\(O(n^3)\),但是可能因为275307894a人傻常数大所以被卡常了,所以我将两个dp写到了一起减小常数。

code:

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=2e2+5,M=1<<16|5,K=400,mod=1e9+7,Mod=mod-1;const db eps=1e-5;const int Iv2=(mod+1)/2;
int n,k,L[N],R[N],A[N<<2],H,W[N],x,y,l,r,op,P[N];ll dp[N],g[N],A1,A2,ToT,B[N],C[N];vector<int> S[N];
void dfs(int x,int La,ll &A1,ll &A2){dp[x]=W[x]=max(0,min(r,R[x])-max(l,L[x])+1);g[x]=P[x]=1ll*(min(r,R[x])+max(l,L[x]))*W[x]/2%mod;A1+=dp[x]*op;A2+=g[x]*op;for(int i:S[x]) i^La&&(dfs(i,x,A1,A2),A1+=op*dp[x]*dp[i]%mod,A2+=op*(dp[x]*g[i]+dp[i]*g[x])%mod,dp[x]=(dp[i]*W[x]+dp[x])%mod,g[x]=(g[x]+g[i]*W[x]+P[x]*dp[i])%mod);} 
ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
ll LG(int n,int k){
	int i,j;ll ToT=0,T1,T2;for(i=0;i<n;i++){
		T1=T2=1;for(j=0;j<n;j++) i^j&&(T1=T1*(k-j)%mod,T2=T2*(i-j)%mod);ToT+=T1*mpow(T2)%mod*B[i]%mod;
	}return ToT%mod;
}
int main(){
	freopen("1.in","r",stdin);
	int i,j;scanf("%d%d",&n,&k);for(i=1;i<=n;i++) scanf("%d%d",&L[i],&R[i]),A[++H]=L[i],A[++H]=R[i]+1,A[++H]=L[i]-k,A[++H]=R[i]+1-k;sort(A+1,A+H+1);H=unique(A+1,A+H+1)-A-1;
	for(i=1;i<n;i++) scanf("%d%d",&x,&y),S[x].PB(y),S[y].PB(x);for(i=1;i<H;i++){
		if(A[i+1]-A[i]-1<=n+4) for(j=A[i];j<A[i+1];j++) op=1,l=j,r=j+k,dfs(1,0,A1,A2),op=-1,l=j+1,dfs(1,0,A1,A2);
		else{B[0]=C[0]=0;for(j=0;j<n+4;j++) j&&(B[j]=B[j-1],C[j]=C[j-1]),op=1,l=A[i]+j,r=A[i]+j+k,dfs(1,0,B[j],C[j]),op=-1,l=A[i]+j+1,dfs(1,0,B[j],C[j]),B[j]%=mod,C[j]%=mod;A1+=LG(n+4,A[i+1]-A[i]-1);Mc(B,C);A2+=LG(n+4,A[i+1]-A[i]-1);}
		//if(A[i+1]-A[i]-1<=n+4) for(j=A[i];j<A[i+1];j++) op=1,l=j,r=j+k,dfs2(1,0,A2),op=-1,l=j+1,dfs2(1,0,A2);
		//else{B[0]=0;for(j=0;j<n+4;j++) j&&(B[j]=B[j-1]),op=1,l=A[i]+j,r=A[i]+j+k,dfs2(1,0,B[j]),op=-1,l=A[i]+j+1,dfs2(1,0,B[j]),B[j]%=mod;A2+=LG(n+4,A[i+1]-A[i]-1);}
	}   printf("%lld\n%lld\n",(A1%mod+mod)%mod,(A2%mod+mod)%mod);
}
posted @ 2022-08-17 15:15  275307894a  阅读(67)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end