T1:树上选一个包含根的连通块,求价格在给定范围内的最大价值(背包)
n<=5e3,背包容量<=1e4
SOL:任轩笛在2018年国集论文中写到的关于树上连通块的一个重要性质:dfs序的转移
具体的,包含根的连通块可以这样表示:
设当前点为v,dfs序为dfs[v],构造一个新图
dfs[v]向dfs[v]+1连边,表示选择这个点,其他点任选
dfs[v]向dfs[v]+siz[v]连边,表示不选这个点,那么这个点的子树也不能选
那么新图中一条1到n+1的路径就表示一种包含1的连通块的选法,新图是一个DAG
那么直接在DAG上按照拓扑序背包一下就完了
Code:
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mp make_pair
#define db double
#define ri register
#define fi first
#define se second
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int INF=1e9,N=1e4+5,M=1e4+5;
struct E{
int vis[N],head[N],nxt[N],v[N],w[N],tot;
E(){tot=0;}
inline void add(int x,int y,int z,int ww){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;v[tot]=z;w[tot]=ww;}
}tr,gr;
int dfn[N],siz[N],sign=0;
int in[N];
void dfs(int v){
dfn[v]=++sign;siz[v]=1;
for(int i=tr.head[v];i;i=tr.nxt[i]){
int y=tr.vis[i];
dfs(y);
siz[v]+=siz[y];
}
}
int dp[5005][M];
int n,p;
int w[N],v[N];
inline void build(){
for(ri int i=1;i<=n;++i){
gr.add(dfn[i],dfn[i]+siz[i],0,0);++in[dfn[i]+siz[i]];
gr.add(dfn[i],dfn[i]+1,v[i],w[i]);++in[dfn[i]+1];
}
}
queue<int>q;
inline void DP(){
q.push(1);
memset(dp,0,sizeof(dp));
while(!q.empty()){
int x=q.front();q.pop();
for(int i=gr.head[x];i;i=gr.nxt[i]){
int y=gr.vis[i],W=gr.w[i],V=gr.v[i];--in[y];
for(ri int j=0;j<=p;++j){
if(x!=1 && !dp[x][j]) continue;
if(j+W<=p) dp[y][j+W]=max(dp[y][j+W],dp[x][j]+V);
}
if(!in[y]) q.push(y);
}
}
int ans=0;
for(int i=0;i<=p;i++) ans=max(ans,dp[n+1][i]);
cout<<ans;
}
inline void file(){freopen("medicine.in","r",stdin);freopen("medicine.out","w",stdout);}
int main(){
n=read();p=read();
for(ri int i=1;i<=n;++i){
w[i]=read();int fa=read();v[i]=read();
if(i!=1) tr.add(fa,i,0,0);
}
dfs(1);build();DP();
return 0;
}
T2:有一棵splay,需要支持三个操作:
1.左旋x
2.右旋x
3.询问x的子树中,所有点的子树权值和之积
n,q<=200000
SOL:简单分析一下,左旋和右旋可以用LCT维护,link和cut会影响的就是当前点到根这条路径的值,会让路径上所有值乘上或除以(乘逆元)一个数,那就是链加,单点询问,LCT做就完了,细节有点多
Code:
#include<bits/stdc++.h>
#define mod 1000000007
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int mul(int x,int y){return (ll)x*y%mod;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
const int N=2e5+5;
namespace LCT{
int lc[N],rc[N];
int w[N],sum[N],ans[N];
int ls[N],rs[N],fa[N],ml[N],f[N];
int que[N];
inline int isrs(int x){return rs[fa[x]]==x;}
inline bool isroot(int x){return ls[fa[x]]!=x && rs[fa[x]]!=x;}
inline void pushmul(int x,int v){
ans[x]=mul(ans[x],v);
ml[x]=mul(ml[x],v);
}
inline void pushdown(int x){
if(ml[x]!=1){
if(ls[x]) pushmul(ls[x],ml[x]);
if(rs[x]) pushmul(rs[x],ml[x]);
ml[x]=1;
}
}
inline void rotate(int x){
int y=fa[x],z=fa[y],b=ls[y]==x?rs[x]:ls[x];
if(z && !isroot(y)) (ls[z]==y?ls[z]:rs[z])=x;
fa[x]=z;fa[y]=x;b?fa[b]=y:0;
if(ls[y]==x) rs[x]=y,ls[y]=b;
else ls[x]=y,rs[y]=b;
}
inline void splay(int x){
que[que[0]=1]=x;
for(int y=x;!isroot(y);y=fa[y]) que[++que[0]]=fa[y];
for(int i=que[0];i;i--) pushdown(que[i]);
while(!isroot(x)){
if(!isroot(fa[x])){
if(isrs(fa[x])==isrs(x)) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
inline void access(int x){for(int y=0;x;y=x,x=fa[x]){splay(x);rs[x]=y;}}
inline void link(int x,int y){access(x);splay(x);fa[x]=y;}
inline void cut(int x,int y){access(y);splay(x);fa[x]=0;}
inline void push(int x,int v){access(x);splay(x);pushmul(x,v);}
inline void init(int n){
sum[0]=sum[n+1]=0;w[n+1]=ans[n+1]=ml[n+1]=ans[0]=ml[0]=1;
for(int i=1;i<=n;i++){
w[i]=read();lc[i]=read();rc[i]=read();
if(lc[i]) f[lc[i]]=fa[lc[i]]=i;
if(rc[i]) f[rc[i]]=fa[rc[i]]=i;
ml[i]=1;
}
f[1]=fa[1]=n+1;lc[n+1]=1;
for(int i=n;i;i--){
sum[i]=add(w[i],add(sum[lc[i]],sum[rc[i]]));
ans[i]=mul(sum[i],mul(ans[lc[i]],ans[rc[i]]));
}
sum[n+1]=add(w[n+1],add(sum[lc[n+1]],sum[rc[n+1]]));
ans[n+1]=mul(sum[n+1],mul(ans[lc[n+1]],ans[rc[n+1]]));
}
inline int ask(int x){if(!x) return 1;splay(x);return ans[x];}
}
using namespace LCT;
inline void zig(int x){
if(!lc[x]) return;
int y=lc[x],p=f[x];
int inv=ksm(sum[y],mod-2);
cut(x,p);cut(y,x);
if(rc[y]) cut(rc[y],y),link(rc[y],x);
link(x,y);link(y,p);
(rc[p]==x?rc[p]:lc[p])=y;
f[y]=p,f[x]=y;if(rc[y]) f[rc[y]]=x;
lc[x]=rc[y],rc[y]=x;
sum[y]=sum[x];
sum[x]=add(w[x],add(sum[lc[x]],sum[rc[x]]));
inv=mul(inv,sum[x]);
push(p,inv);
ans[x]=mul(sum[x],mul(ask(lc[x]),ask(rc[x])));
ans[y]=mul(sum[y],mul(ask(lc[y]),ask(rc[y])));
}
inline void zag(int x){
if(!rc[x]) return;
int y=rc[x],p=f[x];
int inv=ksm(sum[y],mod-2);
cut(x,p);cut(y,x);
if(lc[y]) cut(lc[y],y),link(lc[y],x);
link(x,y);link(y,p);
(rc[p]==x?rc[p]:lc[p])=y;
f[y]=p,f[x]=y;if(lc[y]) f[lc[y]]=x;
rc[x]=lc[y],lc[y]=x;
sum[y]=sum[x];
sum[x]=add(w[x],add(sum[lc[x]],sum[rc[x]]));
inv=mul(inv,sum[x]);
push(p,inv);
ans[x]=mul(sum[x],mul(ask(lc[x]),ask(rc[x])));
ans[y]=mul(sum[y],mul(ask(lc[y]),ask(rc[y])));
}
int main(){
int n=read(),q=read();
init(n);
while(q--){
switch(read()){
case 0:zig(read());break;
case 1:zag(read());break;
case 2:cout<<ask(read())<<"\n";
}
}
return 0;
}
T3:一个网格,你在(0,0),每次可以走到一个以当前点位右下角的mx,my的矩形内,但是不能走到k个位置,这k个位置与当前点的相对距离都是
(
k
i
,
k
i
)
(k_i,k_i)
(ki,ki),且
k
i
k_i
ki是10的倍数,求r步走到目标点的方案数
r<=1000,坐标<=500,k<=50
SOL:dp+容斥,两个维度分开考虑,最后乘起来,不能走的点用组合数+容斥处理掉
Code:
#include<bits/stdc++.h>
#define mod 10007
#define ll long long
#define pb push_back
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return (ll)x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
const int N=1700,M=900;
int fx[N][M],fy[N][M];
int g[N][M/10];
int fac[N],ifac[N];
inline void init(int n){
fac[0]=1;ifac[0]=1;
for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i;i--) ifac[i]=mul(ifac[i+1],i+1);
}
inline int C(int n,int m){if(n<0 && m<0 && n<m) return 0;return mul(fac[n],mul(ifac[m],ifac[n-m]));}
inline int calc(int i,int x,int y){return mul(dec(fx[i][x+1],fx[i][x]),dec(fy[i][y+1],fy[i][y]));}
vector<int>ban;
int main(){
int tx=read(),ty=read(),mx=read(),my=read(),r=read(),k=read();
for(int i=1;i<=k;i++) ban.pb(read());
init(r);
int lim=min(tx,ty)/10;
g[0][0]=1;ban.pb(0);
for(int i=0;i<ban.size();i++) ban[i]/=10;
for(int i=1;i<=r;i++) for(int j=0;j<ban.size();j++) for(int k=lim;k>=ban[j];k--) inc(g[i][k],g[i-1][k-ban[j]]);
for(int i=1;i<=tx+1;i++) fx[0][i]=1;
for(int i=1;i<=r;i++) for(int j=1;j<=tx+1;j++) fx[i][j]=add(fx[i][j-1],dec(fx[i-1][j],fx[i-1][j-min(j-1,mx)-1]));
for(int i=1;i<=ty+1;i++) fy[0][i]=1;
for(int i=1;i<=r;i++) for(int j=1;j<=ty+1;j++) fy[i][j]=add(fy[i][j-1],dec(fy[i-1][j],fy[i-1][j-min(j-1,my)-1]));
int ans=0;
for(int i=0;i<=r;i++){
int op=(i&1)?mod-C(r,i):C(r,i);
for(int j=0;j<=lim;j++) inc(ans,mul(op,mul(g[i][j],calc(r-i,tx-j*10,ty-j*10))));
}
cout<<ans;
return 0;
}