标签:树形背包 通过 return cstring 时间 区间 ora oss 树形dp
日期: |
八月六号 |
总分: |
300分 |
难度: |
提高 ~ 省选 |
得分: |
10分(MMP) |
T1:Tree
T2:异或运算
Emmmmmmm……
一直在打第一题…… 结果考完才发现dp少了一种情况……
除此之外,我无话可说…… Emmmmmm……
树形背包dp,设$f[i][j][k(0/1/2)]$为$i$的子树中,选$j$条边,0:从$i$出发,到$i$结束/1:从$i$出发,到$i$的某个后代结束/2:后代开始,经过$i$,后代结束:
状态转移:
1 f[x][j+k+1][0]=min(f[x][j+k+1][0],f[x][j][0]+f[y][k][0]+2*e[i].dis); 2 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][0]+f[y][k][1]+e[i].dis); 3 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][1]+f[y][k][0]+2*e[i].dis); 4 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][1]+f[y][k][1]+e[i].dis); 5 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][0]+f[y][k][2]+2*e[i].dis); 6 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][2]+f[y][k][0]+2*e[i].dis);
这样就能树形dp,时间复杂度 $O(n^2)$
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 int n,k,x,y,z,tot=0,h[3005],siz[3005]; 7 long long f[3005][3005][3],ans=1e18; 8 struct Edge{ 9 int x,y,dis,next; 10 }e[6005]; 11 bool vis[3005]; 12 13 inline void add_edge(int x,int y,int z){ 14 e[++tot].x=y,e[tot].dis=z; 15 e[tot].next=h[x],h[x]=tot; 16 } 17 18 void dfs(int x,int fa){ 19 siz[x]=1; 20 f[x][0][0]=0; 21 f[x][0][1]=0; 22 for(int i=h[x];i;i=e[i].next){ 23 if(e[i].x==fa)continue; 24 dfs(e[i].x,x); 25 int y=e[i].x; 26 for(int j=siz[x]-1;j>=0;j--) 27 for(int k=siz[y]-1;k>=0;k--){ 28 f[x][j+k+1][0]=min(f[x][j+k+1][0],f[x][j][0]+f[y][k][0]+2*e[i].dis); 29 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][0]+f[y][k][1]+e[i].dis); 30 f[x][j+k+1][1]=min(f[x][j+k+1][1],f[x][j][1]+f[y][k][0]+2*e[i].dis); 31 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][1]+f[y][k][1]+e[i].dis); 32 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][0]+f[y][k][2]+2*e[i].dis); 33 f[x][j+k+1][2]=min(f[x][j+k+1][2],f[x][j][2]+f[y][k][0]+2*e[i].dis); 34 } 35 siz[x]+=siz[e[i].x]; 36 } 37 } 38 39 int main(){ 40 memset(f,0x7f,sizeof(f)); 41 scanf("%d%d",&n,&k); 42 for(int i=1;i<n;i++){ 43 scanf("%d%d%d",&x,&y,&z); 44 add_edge(x,y,z); 45 add_edge(y,x,z); 46 } 47 dfs(1,-1); 48 for(int i=1;i<=n;i++) 49 ans=min(ans,min(f[i][k-1][1],f[i][k-1][2])); 50 printf("%lld",ans); 51 }
可持久化Trie树,我之前还不知道可持久化,现在才知道,这跟主席树是一样的,是通过差分,维护区间。
我们按位贪心,由于$n$很小,对于每一位枚举$a$序列,统计现在的位置往优了走会有多少个值,如果大于$k$,可以走,如果小于$k$,所有反着走。
问题就这么迎刃而解了。(貌似这题用动态开点的主席树维护值域也能做……)
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 int n,m,q,x,y,l,r,k,cnt=0,tot=0; 6 int a[1005],b[300005],root[300005]; 7 struct Trie{ 8 int siz,next[2]; 9 }v[20000005]; 10 struct Node{ 11 int l,r,val; 12 }s[300005]; 13 14 void insert(int &o,int last,int x){ 15 o=++tot; 16 int p=o,q=last; 17 for(int i=31;i>=0;i--){ 18 v[p]=v[q],v[p].siz++; 19 v[p].next[(x>>i)&1]=++tot; 20 p=v[p].next[(x>>i)&1]; 21 q=v[q].next[(x>>i)&1]; 22 } 23 v[p].siz++; 24 } 25 26 int find(int cnt,int k){ 27 int ans=0; 28 for(int i=31;i>=0;i--){ 29 int sum=0; 30 for(int j=1;j<=cnt;j++){ 31 bool c=(s[j].val>>i)&1; 32 sum+=v[v[s[j].r].next[c^1]].siz 33 -v[v[s[j].l].next[c^1]].siz; 34 } 35 if(sum>=k){ 36 ans=ans<<1|1; 37 for(int j=1;j<=cnt;j++){ 38 bool c=(s[j].val>>i)&1; 39 s[j].r=v[s[j].r].next[c^1]; 40 s[j].l=v[s[j].l].next[c^1]; 41 } 42 }else{ 43 k-=sum,ans=ans<<1; 44 for(int j=1;j<=cnt;j++){ 45 bool c=(s[j].val>>i)&1; 46 s[j].r=v[s[j].r].next[c]; 47 s[j].l=v[s[j].l].next[c]; 48 } 49 } 50 } 51 return ans; 52 } 53 54 int main(){ 55 scanf("%d%d",&n,&m); 56 for(int i=1;i<=n;i++)scanf("%d",a+i); 57 for(int i=1;i<=m;i++)scanf("%d",b+i); 58 for(int i=1;i<=m;i++) 59 insert(root[i],root[i-1],b[i]); 60 scanf("%d",&q); 61 for(int i=1;i<=q;i++){ 62 scanf("%d%d%d%d%d",&x,&y,&l,&r,&k); 63 cnt=0; 64 for(int j=x;j<=y;j++) 65 s[++cnt]=(Node){root[l-1],root[r],a[j]}; 66 printf("%d\n",find(cnt,k)); 67 } 68 }
很容易发现离所有点最远的点定是树的直径的两个端点。(我怎么就没想到呢?)
所有点的距离只能在$\left\lceil{d\over 2}\right\rceil$到$d$之间,同时直径上的点两两对称,判断这些即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 6 int n,x,a,b,t[105]; 7 8 int main(){ 9 scanf("%d",&n); 10 for(int i=1;i<=n;i++){ 11 scanf("%d",&x); 12 t[x]++,a=max(a,x); 13 } 14 b=a+1>>1; 15 for(int i=b+1;i<=a;i++) 16 if(t[i]<2){ 17 printf("Impossible"); 18 return 0; 19 } 20 if(a&1){ 21 if(t[b]<2){ 22 printf("Impossible"); 23 return 0; 24 }t[b]-=2; 25 }else{ 26 if(t[b]<1){ 27 printf("Impossible"); 28 return 0; 29 }t[b]-=1; 30 } 31 for(int i=1;i<=b;i++) 32 if(t[i]){ 33 printf("Impossible"); 34 return 0; 35 } 36 printf("Possible"); 37 }
标签:树形背包 通过 return cstring 时间 区间 ora oss 树形dp
原文地址:https://www.cnblogs.com/ezoiLZH/p/9431956.html