2018.8.6 Noip2018模拟测试赛(十九)

日期:

八月六号

 总分:

300分

 难度:

提高 ~ 省选  

 得分:

10分(MMP)

题目目录:

  T1:Tree

  T2:异或运算

  T3:Tree Restoring

赛后反思:

Emmmmmmm……

一直在打第一题…… 结果考完才发现dp少了一种情况……

除此之外,我无话可说…… Emmmmmm……

题解:

T1:Tree

树形背包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)$

COMPLETE CODE:

 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 }

T2:异或运算

可持久化Trie树,我之前还不知道可持久化,现在才知道,这跟主席树是一样的,是通过差分,维护区间。

我们按位贪心,由于$n$很小,对于每一位枚举$a$序列,统计现在的位置往优了走会有多少个值,如果大于$k$,可以走,如果小于$k$,所有反着走。

问题就这么迎刃而解了。(貌似这题用动态开点的主席树维护值域也能做……)

CODE:

 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 }

T3:Tree Restoring

很容易发现离所有点最远的点定是树的直径的两个端点。(我怎么就没想到呢?)

所有点的距离只能在$\left\lceil{d\over 2}\right\rceil$到$d$之间,同时直径上的点两两对称,判断这些即可。

CODE:

 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 }

猜你喜欢

转载自www.cnblogs.com/ezoiLZH/p/9431956.html