T1:
长度为n数组循环k次,求1<=l<=r<=n*k, l~r中不同权值个数的总和。
总长(i+1)*i/2,减去相同数:考虑相同数造成贡献,发现是减去上一个相同权值出现的位置下标。继承之前减去的。
算出长度<n的,>=n恒为cnt.
乘上系数:整个序列出现次数。
注意:n<i<n*2时,只计算跨区间的,还要减去不跨区间的。避免重复也要减去len>=n的。
#include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=b;++i) #define LL long long #define pf(a) printf("%lld ",a) #define phn puts("") using namespace std; #define int LL int read(); #define N 200010 const int mod=1e9+7,inv2=500000004; LL n,K; int a[N],las[N],head[N],f[N]; map<int,int>mp;int cnt; LL qpow(int x,int k){int s=1;x%=mod;for(;k;k>>=1,x=x*x%mod)if(k&1)s=s*x%mod;return s;} signed main(){ // freopen("a.in","r",stdin); n=read();K=read(); F(i,1,n){ a[i]=read(); if(!mp[a[i]])mp[a[i]]=++cnt; a[i]=mp[a[i]]; las[i]=head[a[i]];head[a[i]]=i; } F(i,n+1,n+n){a[i]=a[i-n];las[i]=head[a[i]];head[a[i]]=i;} LL ans=0,w=0,sum=0;/** ans=(ans%mod+mod)%mod;*/ for(LL i=1;i<=n;++i){ sum+=las[i]; w=((i+1)*i/2-sum)%mod; if(i==n){w-=cnt;} f[i]=w; ans=(ans+f[i]*K)%mod; } sum%=mod; for(LL i=n+1;i<n+n;++i){ sum+=las[i]; w=((i+1)*i/2-sum-(i-n+1)*cnt-f[i-n])%mod; ans=(ans+w*(K-1))%mod; // pf(sum);pf(w*(K-1));phn; } LL x=(n*K-n+1)%mod; w=(x+1)*x/2%mod; ans=((ans+w*cnt)%mod+mod)%mod; printf("%lld ",ans); } int read(){ int s=0,f=0;char ch=getchar(); while(!isdigit(ch))f=ch=='-',ch=getchar(); while(isdigit(ch))s=s*10+(ch^48),ch=getchar(); return f?-s:s; } /* g++ 1.cpp ./a.out 3 2 1 2 2 */
T2:
树上差分、前缀和、dfs序的应用。
只询问p,q路径,可以n^2.没必要树形DP。
直接n^2枚举点对O(1)算距离即可。
考虑枚举点对i,j,O(1)统计。
求出以i为lca的路径数和经过i且不以i为lca的路径数。
u=lca(i,j)。
w=u子树和,减去i~j链上,加上u子树外,减去经过u且不以u为lca条数。
先往简单想。别过于套路,一看路径长直接树DP。
好题。
B@哥 真巨。
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; inline void read(int &x) { x=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48,c=getchar(); } typedef long long ll; const int maxn=3005; int n,p,q,tot,dex,fr[maxn],first[maxn],dep[maxn],f[maxn]; ll ans,res,dp[maxn],dis[maxn],g[maxn],h[maxn]; struct Road{ int u,t,nxt; }eage[maxn<<1]; void add(int x,int y) {eage[++tot]=(Road){x,y,first[x]};first[x]=tot;} struct ST_Table{ int p,f[maxn<<1][22],len[maxn<<1],bin[22]; void init() { p=log(dex)/log(2)+1; for(int i=bin[0]=1;i<=p;++i) bin[i]=bin[i-1]<<1; for(int i=1;i<=dex;++i) len[i]=log(i)/log(2); for(int j=1;j<=p;++j) for(int i=1;i+bin[j]-1<=dex;++i) { if(dep[f[i][j-1]]<dep[f[i+bin[j-1]][j-1]]) f[i][j]=f[i][j-1]; else f[i][j]=f[i+bin[j-1]][j-1]; } } int LCA(int x,int y) { if(fr[x]>fr[y]) swap(x,y); int l=len[fr[y]-fr[x]+1]; if(dep[f[fr[x]][l]]<dep[f[fr[y]-bin[l]+1][l]]) return f[fr[x]][l]; else return f[fr[y]-bin[l]+1][l]; } }ST; ll Get_dep(int x,int y) {return dep[x]+dep[y]-2*dep[ST.LCA(x,y)];} void frdfs(int x,int fa) { ST.f[fr[x]=++dex][0]=x;dep[x]=dep[fa]+1;f[x]=fa; for(int i=first[x];i;i=eage[i].nxt) if(eage[i].t!=fa) { frdfs(eage[i].t,x); ST.f[++dex][0]=x; } } void redfs(int x,int fa) { for(int i=first[x];i;i=eage[i].nxt) if(eage[i].t!=fa) { redfs(eage[i].t,x); dp[x]+=dp[eage[i].t]; } } void lsdfs(int x,int fa) { h[x]=h[fa]+g[x]; for(int i=first[x];i;i=eage[i].nxt) if(eage[i].t!=fa) { lsdfs(eage[i].t,x); g[x]+=g[eage[i].t]; } } int main() { read(n);read(p);read(q); for(int i=1,x,y;i<=n-1;++i) { read(x);read(y); add(x,y);add(y,x); } frdfs(1,0); ST.init(); for(int i=1;i<=n;++i) for(int j=1,lca;j<=n;++j) if(Get_dep(i,j)==p) { lca=ST.LCA(i,j);++g[lca]; ++dp[i];++dp[j];--dp[lca];--dp[lca]; } redfs(1,0);lsdfs(1,0); dp[0]=dp[1];g[0]=g[1]; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(Get_dep(i,j)==q) { int lca=ST.LCA(i,j); ans+=g[lca]-(h[i]+h[j]-h[lca]-h[f[lca]]); ans+=g[1]-g[lca]-dp[lca]; /** 经过该点且不以该点为lca的边数。*/ } printf("%lld ",ans); return 0; }
我的n^2递推,利用dfs序预处理lca.
下面是 B&哥 思路:g[i][j],表示以dfs序上ij位置两点为端点路径数。
用来计算u子树外的部分:由于dfs序是连续段,则i,j均不在u子树dfs序范围内即可。
g数组的处理有个细节调了半天,注释上了。
#include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=b;++i) #define LL long long #define pf(a) printf("%d ",a) #define PF(a) printf("%lld ",a) #define phn puts("") using namespace std; int read(); #define N 3005 int n,p,q; int to[N<<1],fir[N<<1],head[N],cnt; void add(int x,int y){to[++cnt]=y;fir[cnt]=head[x];head[x]=cnt;} int fa[N],dep[N],L[N],dfn[N],R[N],tot; void dfs(int x,int Fa){ dep[x]=dep[Fa]+1;fa[x]=Fa;L[x]=++tot;dfn[tot]=x; for(int i=head[x],v;i;i=fir[i])if(!dep[v=to[i]]){ dfs(v,x); } R[x]=tot; } int lca[N][N]; LL w[N],g[N][N],sub[N],up[N],ans; void push(int x){ sub[x]=w[x];up[x]=up[fa[x]]+w[x]; for(int i=head[x],v;i;i=fir[i])if((v=to[i])^fa[x]){ push(v);sub[x]+=sub[v]; } } /*g++ 2.cpp ./a.out */ void cal(int x,int y){ int u=lca[x][y]; ans+=sub[u]; ans-=up[x]+up[y]-up[u]-up[fa[u]]; int l=L[u],r=R[u]; ans+=g[l-1][l-1]; ans+=g[l-1][n]-g[l-1][r]; ans+=g[n][l-1]-g[r][l-1]; ans+=g[n][n]-g[n][r]-g[r][n]+g[r][r]; // if(x==3&&y==5){PF(ans);} } int main(){ freopen("b.in","r",stdin); // freopen("2.in","r",stdin);//freopen("2.out","w",stdout); n=read();p=read();q=read(); for(int i=1,u,v;i<n;++i){ u=read();v=read();add(u,v);add(v,u); } dfs(1,0); F(i,1,n){ lca[i][i]=i; F(j,L[i]+1,R[i])lca[i][dfn[j]]=lca[dfn[j]][i]=i; for(int j=L[i]+1;j<=R[i];j=R[dfn[j]]+1){ for(int k=j;k<=R[dfn[j]];++k){ for(int h=R[dfn[j]]+1;h<=R[i];++h){ lca[dfn[k]][dfn[h]]=lca[dfn[h]][dfn[k]]=i; } } } } F(i,1,n){ F(j,1,n){ if(dep[i]+dep[j]-dep[lca[i][j]]*2==q){ ++w[lca[i][j]];++g[L[i]][L[j]]; /** 注意:这里是g[L[i]],加到dfs序对应位置。*/ } } } F(i,1,n){ F(j,1,n){ g[i][j]=g[i][j-1]+g[i-1][j]+g[i][j]-g[i-1][j-1]; } } // F(i,1,n)pf(dfn[i]);phn;phn; // F(i,1,n){F(j,1,n)PF(g[i][j]);phn;}phn; push(1); F(i,1,n){ F(j,1,n){ if(dep[i]+dep[j]-dep[lca[i][j]]*2==p){ cal(i,j); // pf(i);pf(j);pf(ans);phn; } } } printf("%lld ",ans); } int read(){ int s=0,f=0;char ch=getchar(); while(!isdigit(ch))f=ch=='-',ch=getchar(); while(isdigit(ch))s=s*10+(ch^48),ch=getchar(); return f?-s:s; } /* g++ d2.cpp ./a.out g++ 2.cpp ./a.out g++ bsgs.cpp ./a.out 10 2 3 2 1 3 1 4 1 5 3 6 3 7 6 8 6 9 6 10 7 g++ 2.cpp ./a.out 8 2 2 1 2 1 3 2 4 2 5 4 6 4 7 5 8 g++ 2.cpp ./a.out 5 2 1 1 2 2 3 3 4 2 5 g++ 2.cpp ./a.out 4 1 1 1 2 2 3 3 4 */
T3:
DP:f[j][k]表示最大值为j,权值k次方的和。
考虑在已经扫过的一段序列后面增加一个的贡献:
w^k->(w+1)^k
=sigma:w^h*C(k,h).
则对于每个k次方可以求和,共同计算。
纯DP N^2.线段树优化,类似队长快跑。
#include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=b;++i) #define LL long long #define pf(a) printf("%d ",a) #define PF(a) printf("%lld ",a) #define phn puts("") using namespace std; #define int LL int read(); #define N 100010 #define int LL const int mod=1e9+7; int n,m,a[N],f[N][22],sum[22],plu[22],C[22][22]; int MO(int x){return x<mod?x:x-=mod;} /** DP:f[j][k]表示最大值为j,权值k次方的和。 考虑在已经扫过的一段序列后面增加一个的贡献: w^k->(w+1)^k =sigma:w^h*C(k,h). 则对于每个k次方可以求和,共同计算。 */ struct SEG{ struct nodi{ int l,r,g,w[22]; }s[N<<2]; #define sx s[x] #define lc s[x<<1] #define rc s[x<<1|1] #define MIT int z=sx.l+sx.r>>1 void biu(int x,int l,int r){ sx.l=l;sx.r=r;sx.g=1; if(l==r)return ;MIT; biu(x<<1,l,z);biu(x<<1|1,z+1,r); } void down(int x){ if(sx.g>1){ F(i,0,20){lc.w[i]=lc.w[i]*sx.g%mod;rc.w[i]=rc.w[i]*sx.g%mod;} lc.g=lc.g*sx.g%mod;rc.g=rc.g*sx.g%mod;sx.g=1; } } void up(int x){ F(i,0,20)sx.w[i]=MO(lc.w[i]+rc.w[i]); } void mpl(int x,int l,int r){ if(l<=sx.l&&sx.r<=r){ F(i,0,20)sx.w[i]=sx.w[i]*2%mod; sx.g=sx.g*2%mod; return ; }MIT;down(x); if(l<=z)mpl(x<<1,l,r);if(r>z)mpl(x<<1|1,l,r);up(x); } void ask(int x,int l,int r){ if(l<=sx.l&&sx.r<=r){ F(i,0,20)sum[i]=MO(sum[i]+sx.w[i]); return ; }MIT;down(x); if(l<=z)ask(x<<1,l,r);if(r>z)ask(x<<1|1,l,r); } void add(int x,int p){ if(sx.l==sx.r){ F(i,0,20)sx.w[i]=MO(sx.w[i]+plu[i]); return ; }MIT;down(x); add((p<=z?(x<<1):(x<<1|1)),p);up(x); } }sg; signed main(){ // freopen("c.in","r",stdin); /** 区间乘2,单点加,区间求和。*/ n=read();m=read(); F(i,1,n)a[i]=read(); C[0][0]=1; F(i,1,20){ C[i][0]=1;F(j,1,i)C[i][j]=MO(C[i-1][j-1]+C[i-1][j]); } sg.biu(1,1,n); F(i,1,n){ F(k,0,m)plu[k]=1; // F(j,a[i]+1,n)F(k,0,m)f[j][k]=MO(f[j][k]<<1); sg.mpl(1,a[i]+1,n); // F(j,1,a[i]-1)F(k,0,m)w[k]+=f[j][k]; sg.ask(1,1,a[i]-1); F(k,0,m){ F(h,0,k){ plu[k]=(plu[k]+sum[h]*C[k][h])%mod; } } sg.add(1,a[i]); F(k,0,m)sum[k]=plu[k]=0; } LL ans=(sg.s[1].w[m]%mod+mod)%mod; printf("%lld ",ans); } int read(){ int s=0,f=0;char ch=getchar(); while(!isdigit(ch))f=ch=='-',ch=getchar(); while(isdigit(ch))s=s*10+(ch^48),ch=getchar(); return f?-s:s; } /* g++ 3.cpp ./a.out 3 2 1 3 2 */