9.17题解

我们搬家了,被从政新正心这两个字似乎是这么写的楼驱逐到了扬帆楼,回到了级部的视线范围内

T1

考场上并没有发现什么性质,就用二分答案加$set$贪心水了过去,如果二分答案,那么我们对于每个人一定是去找远离终点方向上可以打的最远的怪去打,这样的话给别的人留下的合法的机会就更多,如果这个怪在远离终点的方向上没有怪可打了,就找靠近终点方向上最近的怪去打,如果两个方向上都没有可打的合法的怪了,直接$return$ $false$就可以了,分为在终点左边的和右边的两种情况,搞一搞就可以了,复杂度是$O(nlognlogAns)$,$Ans$代表答案区间,$n$是每次$check$时枚举每个人,$logn$是$set$自带的,毒瘤出题人并没有把最大的数据放在最后一组,导致我并没有看到最大的,然后我数组就开小了,我就$RE90$了我太难了

正解似乎是个dp

 1 #include<set>
 2 #include<cmath>
 3 #include<cstdio>
 4 #include<iostream>
 5 #include<algorithm>
 6 #define maxn 25100
 7 #define maxm 50100
 8 #define int long long
 9 #define inff 3000000000
10 #define inf 2000000000000000000
11 using namespace std;
12 int n,m,S,l,r=inff,ans=inf;
13 int p[maxn],q[maxm];
14 set <int> s;
15 bool check(int len)
16 {
17     s.clear();
18     for(int i=1;i<=m;++i)  s.insert(q[i]);
19     for(int i=1;i<=n;++i)
20     {
21         if(p[i]>S)  break;
22         set<int>::iterator pos=s.lower_bound(p[i]);
23         set<int>::iterator ls=pos;
24         if(*pos<p[i]&&p[i]-*pos+abs(S-*pos)>len)  return 0;
25         if(*ls>p[i])  ls--;//远离s处的点,先找到最近的一个
26         //远离s的方向找最远的,靠近s的方向找最近的
27         if(pos==s.begin()||p[i]-*ls+S-*ls>len)
28         {
29             if(*pos-p[i]+abs(S-*pos)>len)  return 0;
30             else  s.erase(*pos);
31             continue;
32         }
33         while(1)
34         {
35             if(p[i]-*ls+S-*ls>len)  {ls++;  break;}
36             else
37             {
38                 if(ls==s.begin())  break;
39                 ls--;
40             }
41         }
42         s.erase(*ls);    
43     }
44     for(int i=n;i>=1;--i)
45     {
46         if(p[i]<S)  break;
47         set<int>::iterator pos=s.lower_bound(p[i]);
48         if(pos==s.end()||*pos-p[i]+abs(S-*pos)>len)
49         {
50             pos--;
51             if(p[i]-*pos+abs(S-*pos)>len)  return 0;
52             else  s.erase(*pos);
53             continue;
54         }
55         while(1)
56         {
57             if(*pos-p[i]+*pos-S>len)  {pos--;  break;}
58             else
59             {
60                 pos++;
61                 if(pos==s.end())  {pos--;  break;}
62             }
63         }
64         s.erase(*pos);
65     }
66     return 1;
67 }
68 signed main()
69 {
70     scanf("%lld%lld%lld",&n,&m,&S);
71     for(int i=1;i<=n;++i)  scanf("%lld",&p[i]);
72     for(int i=1;i<=m;++i)  scanf("%lld",&q[i]);
73     sort(p+1,p+n+1);
74     while(l<r)
75     {
76         if(l==r-1)
77         {
78             if(check(l))  ans=min(ans,l);
79             else  ans=min(ans,r);
80             break;
81         }
82         int mid=(l+r)>>1;
83         if(check(mid))  {ans=min(ans,mid);  r=mid;}
84         else  l=mid+1;
85     }
86     printf("%lld
",ans);
87     return 0;
88 }
View Code

T2

容易发现我一定是尽量让每一个点都往上配,走的越向上,对答案的贡献就越大,而是谁走上去的并不重要,所以我们可以在$dfs$的过程中,尽量的让每个点都沿着他的父亲向上走,如果在当前子树外面的点少于$k$个了,就证明这个子树中的一些点必须留下,就让可以配的点继续向上配就可以了,也算是个贪心吧

 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxn 100100
 4 #define int long long
 5 using namespace std;
 6 int n,k,a,js,ans;
 7 int head[maxn],to[maxn*2],xia[maxn*2];
 8 int pd[maxn],size[maxn];
 9 void add(int x,int y)
10 {
11     to[++js]=y;  xia[js]=head[x];  head[x]=js;
12 }
13 void dfs(int x)
14 {
15     pd[x]=1;
16     for(int i=head[x];i;i=xia[i])
17     {
18         int ls=to[i];
19         if(pd[ls])  continue;
20         dfs(ls);
21         if(size[ls]<=k)  ans+=size[ls];
22         else  ans+=2*k-size[ls];
23         size[x]+=size[ls];
24     }
25 }
26 signed main()
27 {
28     //freopen("beauty.in","r",stdin);
29     scanf("%lld%lld%lld",&n,&k,&a);
30     for(int i=1;i<=2*k;++i)  {int v;  scanf("%lld",&v);  size[v]=1ll;}
31     for(int i=1;i<n;++i)
32     {
33         int u,v;  scanf("%lld%lld",&u,&v);
34         add(u,v);  add(v,u);
35     }
36     dfs(1);  printf("%lld
",ans);
37     return 0;
38 }
View Code

正解看起来还是个dp的样子

T3

听说是树剖,我不会,本来想打部分分,结果一直过不去样例,就死了

咕咕咕咕咕咕咕咕咕咕.........

原文地址:https://www.cnblogs.com/hzjuruo/p/11644489.html