- 上午
- BZOJ 1036 [ZJOI2008]树的统计Count
这是一个裸的 树链剖分+线段树
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 30005 #define INF 0x3f3f3f3f #define ls lson[u] #define rs rson[u] #define get(a,u,l,r,al,ar,kd); if(kd==0) a=max(a,query(u,l,r,al,ar,kd)); else a+=query(u,l,r,al,ar,kd); using namespace std; struct edge{ int to,next; }e[MAXN<<1]; int lson[MAXN<<1],rson[MAXN<<1],mxn[MAXN<<1],sum[MAXN<<1]; int head[MAXN],sze[MAXN],top[MAXN],val[MAXN],bs[MAXN],xl[MAXN],mp[MAXN],dep[MAXN],fa[MAXN]; int n,m,ent=1,cnt,sz,rt; void add(int u,int v){ e[ent]=(edge){v,head[u]}; head[u]=ent++; } void find_heavy(int u,int f,int deep){ sze[u]=1; dep[u]=deep; fa[u]=f; int mxs=0; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa[u]) continue; find_heavy(v,u,deep+1); sze[u]+=sze[v]; if(sze[v]>mxs) mxs=sze[v],bs[u]=v; } } void connect_heavy(int u,int tp){ top[u]=tp; xl[++cnt]=u; mp[u]=cnt; if(bs[u]) connect_heavy(bs[u],tp); for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa[u]||v==bs[u]) continue; connect_heavy(v,v); } } void pushup(int u){ mxn[u]=max((ls?mxn[ls]:-INF),(rs?mxn[rs]:-INF)); sum[u]=(ls?sum[ls]:0)+(rs?sum[rs]:0); } void build(int &u,int l,int r){ u=++sz; if(l==r){ mxn[u]=sum[u]=val[xl[l]]; return ; } int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); pushup(u); } void modify(int u,int l,int r,int p,int w){ if(l==r){ mxn[u]=sum[u]=w; return; } int mid=(l+r)>>1; if(p<=mid) modify(ls,l,mid,p,w); else modify(rs,mid+1,r,p,w); pushup(u); } void change(int a,int b){ int p=mp[a]; modify(rt,1,n,p,b); } int query(int u,int l,int r,int al,int ar,int kd){ if(al<=l&&r<=ar) return kd?sum[u]:mxn[u]; int mid=(l+r)>>1,now=kd?0:-INF; if(al<=mid) {get(now,ls,l,mid,al,ar,kd);} if(mid<ar) {get(now,rs,mid+1,r,al,ar,kd);} return now; } void answer(int a,int b,int kd){ int ans=kd?0:-INF,l,r; while(top[a]!=top[b]){ if(dep[top[a]]>dep[top[b]]) swap(a,b); l=mp[top[b]]; r=mp[b]; if(l>r) swap(l,r); get(ans,rt,1,n,l,r,kd); b=fa[top[b]]; } l=mp[a]; r=mp[b]; if(l>r) swap(l,r); get(ans,rt,1,n,l,r,kd); printf("%d ",ans); } int main(){ scanf("%d",&n); for(int i=1,a,b;i<n;i++){ scanf("%d%d",&a,&b); add(a,b); add(b,a); } for(int i=1;i<=n;i++) scanf("%d",&val[i]); find_heavy(1,0,1); connect_heavy(1,1); build(rt,1,n); scanf("%d",&m); char com[10]; for(int i=1,a,b;i<=m;i++){ scanf(" %s %d%d",com,&a,&b); if(com[1]=='H') change(a,b); else if(com[1]=='M') answer(a,b,0); else if(com[1]=='S') answer(a,b,1); } return 0; }
- BZOJ 1036 [ZJOI2008]树的统计Count
- BZOJ 1037 [ZJOI2008]生日聚会Party
DP神题
dp[i][j][k][l]:放了i个人,有j个男的,
且以i为结尾的所有区间中:男的最多比女的多k个,女的最多比男的多l个 的方案数代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int mod=12345678; int dp[305][155][25][25]; int n,m,p,ans; int main(){ scanf("%d%d%d",&n,&m,&p); dp[0][0][0][0]=1; for(int i=0;i<n+m;i++) for(int j=0;j<=n;j++) for(int k=0;k<=p;k++) for(int l=0;l<=p;l++){ if(j+1<=n&&k+1<=p) dp[i+1][j+1][k+1][max(l-1,0)]=(dp[i+1][j+1][k+1][max(l-1,0)]+dp[i][j][k][l])%mod; if(i-j+1<=m&&l+1<=p) dp[i+1][j][max(k-1,0)][l+1]=(dp[i+1][j][max(k-1,0)][l+1]+dp[i][j][k][l])%mod; } for(int k=0;k<=p;k++) for(int l=0;l<=p;l++) ans=(ans+dp[n+m][n][k][l])%mod; printf("%d",ans); return 0; }
- 晚上