1. 程式人生 > >2018.8.6 Noip2018模擬測試賽(十九)

2018.8.6 Noip2018模擬測試賽(十九)

() strong 多少 sin 持久 省選 lin main noip

日期:

八月六號

總分:

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 }

2018.8.6 Noip2018模擬測試賽(十九)