2019 Multi-University Training Contest 3 题解

1001 - 深海里的大菠萝

显然这么小的数据范围我们瞎暴力就行了。然后就能得出来凸包上合法的点对。然后我贪心wa了,发现了反例,感觉是dp,不会dp,卒

其实dp很简单,我们在普通的区间dp上枚举断点更新完答案之后再看区间首尾合不合法即可。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef double db;
 4 const db eps=1e-10;
 5 int sign(db k){
 6     if (k>eps) return 1; else if (k<-eps) return -1; return 0;
 7 }
 8 int cmp(db k1,db k2){return sign(k1-k2);}
 9 int inmid(db k1,db k2,db k3){return sign(k1-k3)*sign(k2-k3)<=0;}
10 struct point{
11     db x,y;
12     point operator + (const point &k1) const{return (point){k1.x+x,k1.y+y};}
13     point operator - (const point &k1) const{return (point){x-k1.x,y-k1.y};}
14     point operator * (db k1) const{return (point){x*k1,y*k1};}
15     point operator / (db k1) const{return (point){x/k1,y/k1};}
16     int operator == (const point &k1) const{return cmp(x,k1.x)==0&&cmp(y,k1.y)==0;}
17     bool operator < (const point k1) const{
18         int a=cmp(x,k1.x);
19         if (a==-1) return 1; else if (a==1) return 0; else return cmp(y,k1.y)==-1;
20     }
21     db abs(){return sqrt(x*x+y*y);}
22     db abs2(){return x*x+y*y;}
23     db dis(point k1){return ((*this)-k1).abs();}
24 };
25 int inmid(point k1,point k2,point k3){return inmid(k1.x,k2.x,k3.x)&&inmid(k1.y,k2.y,k3.y);}
26 db cross(point k1,point k2){return k1.x*k2.y-k1.y*k2.x;}
27 db dot(point k1,point k2){return k1.x*k2.x+k1.y*k2.y;}
28 point proj(point k1,point k2,point q){ // q 到直线 k1,k2 的投影
29     point k=k2-k1; return k1+k*(dot(q-k1,k)/k.abs2());
30 }
31 db disSP(point k1,point k2,point q){
32     point k3=proj(k1,k2,q);
33     if (inmid(k1,k2,k3)) return q.dis(k3); else return min(q.dis(k1),q.dis(k2));
34 }
35 vector<point> ConvexHull(vector<point>A,int flag=0){ // flag=0 不严格 flag=1 严格
36     int n=A.size(); vector<point>ans(n*2);
37     sort(A.begin(),A.end()); int now=-1;
38     for (int i=0;i<A.size();i++){
39         while (now>0&&sign(cross(ans[now]-ans[now-1],A[i]-ans[now-1]))<flag) now--;
40         ans[++now]=A[i];
41     } int pre=now;
42     for (int i=n-2;i>=0;i--){
43         while (now>pre&&sign(cross(ans[now]-ans[now-1],A[i]-ans[now-1]))<flag) now--;
44         ans[++now]=A[i];
45     } ans.resize(now); return ans;
46 }
47 int T,n,g;point o[105];db R;
48 int dp[404][404],b[404][404];
49 vector<point> v;point p;
50 bool can(point a,point b) {
51     for (int i = 0; i < g; i++){
52         if(cmp(disSP(a,b,o[i]),R)<=0)
53             return false;
54     }
55     return true;
56 }
57 int main(){
58     scanf("%d",&T);
59     while (T--){
60         v.clear();
61         memset(b,0, sizeof(b));
62         memset(dp,0, sizeof(dp));
63         scanf("%d%d%lf",&n,&g,&R);
64         for(int i=1;i<=n;i++){
65             scanf("%lf%lf",&p.x,&p.y);
66             v.push_back(p);
67         }
68         for(int i=0;i<g;i++){
69             scanf("%lf%lf",&o[i].x,&o[i].y);
70         }
71         v=ConvexHull(v);
72         int m = v.size();
73         for(int i=0;i<m-1;i++){
74             for(int j=i+2;j<m;j++){
75                 if(i==0&&j==m-1)continue;
76                 if(can(v[i],v[j])) {
77                     b[i][j]=b[j][i]=1;
78                 }
79             }
80         }
81         for(int len=3;len<=m;len++){
82             for(int i=0;i+len-1<m;i++){
83                 int ed = i+len-1;
84                 for(int j=i;j<=ed;j++){
85                     dp[i][ed]=max(dp[i][ed],dp[i][j]+dp[j][ed]);
86                 }
87                 dp[i][ed]+=b[i][ed];
88             }
89         }
90         printf("%d\n",dp[0][m-1]);
91     }
92 }
View Code

 

1012 - Yukikaze and Demons

dag上的支配集,增加汇点将所有出度为零的点汇聚,反向建图拓扑排序,如果点x要被ban掉,不能到达汇点,则需要x的所有父亲的lca被ban掉,所以求出其lca,将其挂在lca下面即可。如此建树。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1e5+5;
int T, n, m, indeg[maxn], par[maxn][20], depth[maxn];
vector<int>g[maxn], p[maxn];
queue<int>q;
void init(){
    memset(par,-1,sizeof(par));
    for(int i=1;i<=n+1;i++) g[i].clear(),p[i].clear(), indeg[i]=0;
}
int lca(int u, int v){
    if(depth[u]<depth[v]) swap(u,v);
    int t=depth[u]-depth[v];
    for(int i=0;i<=18;i++){
        if(t&(1<<i)) u=par[u][i];
    }
    if(u==v) return u;
    for(int i=18;i>=0;i--){
        if(par[u][i]!=par[v][i]) u=par[u][i], v=par[v][i];
    }
    return par[u][0];
}
void update(int u, int fa){
    par[u][0]=fa; depth[u]=depth[fa]+1;
    for(int i=1;i<=18;i++){
        if(par[u][i-1]==-1) par[u][i]=-1;
        else par[u][i]=par[par[u][i-1]][i-1];
    }
}
void topsort(){
    q.push(n);
    while(!q.empty()){
        int u=q.front(); q.pop();
        if(u!=n){
            int fa=p[u][0];
            for(int i=1;i<p[u].size();i++){
                fa=lca(fa,p[u][i]);
            }
            update(u,fa);
        }
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i]; indeg[v]--;
            if(!indeg[v]) q.push(v);
        }
    }
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m); init(); int u, v;
        for(int i=1;i<=m;i++) scanf("%d%d",&u,&v), g[v].push_back(u), indeg[u]++, p[u].push_back(v);
        for(int i=1;i<=n;i++) if(!indeg[i]) g[n+1].push_back(i), p[i].push_back(n+1), indeg[i]++;
        n++; depth[n]=0;
        topsort();
        int q; scanf("%d",&q);
        while(q--){
            scanf("%d%d",&u,&v);
            printf("%d\n",depth[u]+depth[v]-depth[lca(u,v)]);
        }
    }
    return 0;
}
View Code

1011 - Squrirrel

$dp$。如果不删边,那么这个题就是个很经典的原题,现在删边的话,其实就是在不删边的基础上进行一定的修改和维护。我们用$down[u][0]$和$down[u][1]$表示节点$u$向子树走的不删边的最长路和删边的最长路,用$up[u][0]$和$up[u][1]$表示向父亲走的对应的最长路。对于$down[u][0]$,转移很显然,现在考虑$down[u][1]$,如果要删除边,我们一定会选择$down[u][0]$对应的那棵子树的一条边,因为这样才能缩短最长路,但是我们不能就把缩短后的最长路当做是$down[u][1]$,因为也许去往其他子树的不删边的最长路比它更长,所以,我们在求$down[u][0]$时,不仅要求最长路,还要求一个次长路,且这两条路去往不同的子树,最后$down[u][1]$就是在次长路和最长路对应的那棵子树删边后的最长路之间取最大值。$up[u][0]$和$up[u][1]$的求法稍稍复杂点,设$f$是$u$的父亲,对于$up[u][0]$,它分两种情况,一种是它先到$f$,再往$f$的祖先方向走,另一种是它先到$f$,再往$f$的另一颗子树走,前一种情况转移显然,后一种的话,前面维护的最长路和次长路再次起作用,这里不赘述。而$up[u][1]$,将会更复杂些,为了更容易的分析,我们不妨以$f$为根,并且删除$u$这棵子树,这样的话,其实就等价于在新的树中对$f$求$down[f][0]$和$down[f][1]$,最后加上$f$到$u$的边权即可(也可能选择删这条边,但不影响)。前面说了这样需要维护最大值和次大值,然后就很容易求了,但是呢,我们不能真的删除$u$对应的子树,所以$u$这棵子树的影响我们需要想办法排除,因此,我们不妨维护前三大值,如果$u$这棵子树对应的值在前三大值中,直接删去,就可以得到实际用到的最大值的次大值,其他的就和前面一样了,具体细节看代码。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<cstring>
 5 const int N=2e5+5;
 6 int down[N][2][2];
 7 int up[N][2];
 8 int T,n;
 9 typedef std::pair<int,int> P;
10 std::vector<P> g[N];
11 void init() {
12     for(int i=1;i<=n;i++) g[i].clear();
13 }
14 void dfs(int u,int fa) {
15     memset(down[u],0,sizeof(down[u]));
16     for(P c:g[u]) if(c.first!=fa){
17         int v=c.first;
18         dfs(v,u);
19         int L=down[v][0][0]+c.second;
20         if(L>down[u][0][0]) {
21             down[u][0][1]=down[u][0][0];
22             down[u][0][0]=L;
23         }
24         else if(L>down[u][0][1]) {
25             down[u][0][1]=L;
26         }
27     }
28     for(P c:g[u]) if(c.first!=fa) {
29         int v=c.first;
30         int L=down[v][0][0]+c.second,R=std::min(down[v][1][0]+c.second,down[v][0][0]);
31         if(L==down[u][0][0]) {
32             down[u][1][0]=std::max(R,down[u][0][1]);
33             break;
34         }
35     }
36     //printf("%d %d %d %d\n",u,down[u][0][0],down[u][0][1],down[u][1][0]);
37 }
38 void dfs2(int u,int fa) {
39     P s[3];
40     s[0]=P(up[u][0],up[u][1]);
41     s[1]=s[2]=P(0,0);
42     for(P c:g[u]) if(c.first!=fa) {
43         int v=c.first;
44         P t(down[v][0][0]+c.second,std::min(down[v][1][0]+c.second,down[v][0][0]));
45         if(t>s[0]) {
46             s[2]=s[1];
47             s[1]=s[0];
48             s[0]=t;
49         }
50         else if(t>s[1]) {
51             s[2]=s[1];
52             s[1]=t;
53         }
54         else if(t>s[2]) s[2]=t;
55     }
56     for(P c:g[u]) if(c.first!=fa) {
57         int v=c.first;
58         up[v][0]=up[u][0]+c.second;
59         int L=down[v][0][0]+c.second;
60         if(down[u][0][0]==L) up[v][0]=std::max(up[v][0],c.second+down[u][0][1]);
61         else up[v][0]=std::max(up[v][0],c.second+down[u][0][0]);
62         P t(down[v][0][0]+c.second,std::min(down[v][1][0]+c.second,down[v][0][0]));
63         int p=-1;
64         static int l,r;
65         for(int i=0;i<3;i++) if(t==s[i]) p=i;
66         up[v][1]=p?s[0].first:s[1].first;
67         l=p?0:1;
68         for(int i=0;i<3;i++) {
69             if(i==l||i==p) continue;
70             r=i;break;
71         }
72         up[v][1]=std::min(up[v][1],std::max(s[l].second,s[r].first)+c.second);
73         dfs2(v,u);
74     }
75     //printf("--%d %d %d\n",u,up[u][0],up[u][1]);
76 }
77 int main() {
78     scanf("%d",&T);
79     while(T--) {
80         scanf("%d",&n);
81         init();
82         for(int i=1,u,v,w;i<n;i++) {
83             scanf("%d%d%d",&u,&v,&w);
84             g[u].push_back(P(v,w));
85             g[v].push_back(P(u,w));
86         }
87         dfs(1,0);up[1][0]=up[1][1]=0;
88         dfs2(1,0);
89         int ans=1,val=std::min(std::max(down[1][0][0],up[1][1]),std::max(down[1][1][0],up[1][0]));
90         for(int i=2;i<=n;i++) {
91             int tmp=std::min(std::max(down[i][0][0],up[i][1]),std::max(down[i][1][0],up[i][0]));
92             if(tmp<val) ans=i,val=tmp;
93         }
94         printf("%d %d\n",ans,val);
95     }
96     return 0;
97 }
View Code

猜你喜欢

转载自www.cnblogs.com/Onlymyheart/p/11267401.html