10.16考试反思

Contest2171 - 2019年10月多校联训B层测试12

 

T1 妹子

http://www.accoders.com/problem.php?cid=2171&pid=0

T1看似是个非常弱智的题,实际上非常具有思考价值,不光需要考虑每个边的关系,还需要考虑这个盒子能不能斜着放到另一个盒子里,如果没判,就很GG的70分

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 #define res register 
 4 #define MAXN 200050
 5 #define int ll 
 6 #define db double
 7 int n,m,a,b,c,d;
 8 double k1,k2;
 9 using namespace std;
10 ll read()
11 {
12     int s=0,w=1;char ch=getchar();
13     while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
14     while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();}
15     return s*w;
16 }
17 signed main()
18 {
19     n=read();
20     for(res int i=1;i<=n;i++){
21         a=read(),b=read(),c=read(),d=read();
22         k1=sqrt(a*a*1.0+1.0*b*b);k2=sqrt(c*c*1.0+d*d*1.0);
23         if(b<a) std::swap(a,b);if(d<c) std::swap(c,d);
24         if(k1<k2) std::swap(k1,k2),swap(a,c),swap(b,d);
25         if(k2<b) {if(a>=c) cout<<"Yes"<<endl;else cout<<"No"<<endl;}
26         else{
27             double k=sqrt(k2*k2-b*b*1.0);
28             if(k<=c){if(a>=c) cout<<"Yes"<<endl;else cout<<"No"<<endl;continue;}
29             db x=(k+a*1.0)/2,y=(a*1.0-k)/2;
30             db kk=sqrt(d*d*1.0-x*x);
31             db r=(b*1.0-kk);
32             if(r*r+y*y<c*c) puts("No");else puts("Yes");
33         }
34     }
35 }               
View Code

 

T2 旅程

http://www.accoders.com/problem.php?cid=2171&pid=1

首先看数据范围n<=200,还需要求最短路,显然是Floyd没跑了。然后我们考虑对于修改进行离线处理,然后修改,正序删边就相当于倒叙加边,我们考虑对于x-y的最短路,我们考虑,如果加上一条边之后,两点之间的最短路发生变化,显然是x-i的最短路i-j的最短路以及j-y的最短路之和,所以我们可以考虑枚举i和j,枚举之后O(1)查询,然后跟已有的最短路比较,如果优,就更新即可

#include <bits/stdc++.h>
#define ll long long
#define res register 
#define MAXN 300050
#define int ll 
using namespace std;
bool ju;
int x,y,z,n,m,w[MAXN],diss[2500][2500],as,op[200050],gg[2500][2500],top,xx[200050],yy[200050],cnt,ans[MAXN];
inline void Floyd(){
    for(res int k=1;k<=n;k++)
    for(res int i=1;i<=n;i++)
    for(res int j=1;j<=n;j++)
    {
        if(diss[i][j]>diss[i][k]+diss[k][j])
        diss[i][j]=diss[i][k]+diss[k][j];
    }
}
inline void rebuild(){
    for(res int i=1;i<=n;i++)
    for(res int j=1;j<=n;j++)
    if(gg[i][j]==0) diss[i][j]=0x7f7f7f;
    else diss[i][j]=gg[i][j];
}
ll read()
{
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();}
    return s*w;
}
signed main()
{
    n=read(),m=read();
    for(res int i=1;i<=n;i++)
    for(res int j=1;j<=n;j++){
        gg[i][j]=read();
        if(gg[i][j]==0) diss[i][j]=0;
        else diss[i][j]=gg[i][j];
    }
    for(res int i=1;i<=m;i++)
    {
        op[i]=read(),xx[i]=read();yy[i]=read();
        if(op[i]==1) 
        diss[xx[i]][yy[i]]=0x3f3f3f3f,w[i]=gg[xx[i]][yy[i]];
    }
    Floyd();
    for(res int i=m;i>=1;i--){
        int dx=xx[i],dy=yy[i];
        if(op[i]==2) ans[++cnt]=diss[dx][dy];
        else
        for(res int j=1;j<=n;j++)
        for(res int k=1;k<=n;k++){
            int dis=diss[j][dx]+diss[dy][k]+w[i];
            if(dis<diss[j][k]) diss[j][k]=dis;
        }
    }
    for(res int i=cnt;i>=1;i--) printf("%lld\n",ans[i]>=0x3f3f3f3f?-1:ans[i]);
    return 0;
}
View Code

 

总结,首先对于正序删边的问题,我们可以考虑将其转化为倒叙加边的问题,这样会更好处理,然后对于这种带修改的问题,我们往往会选择离线的手段解决问题。然后由Floyd的性质可以n3求出一个图的任意两点最短路,我们就方便每次n2枚举O1查询了

 

T3 老大

http://www.accoders.com/problem.php?cid=2171&pid=2

首先审题,需要最大值最小,我们第一思路就是二分然后审题,我们需要把两个奖杯放在不同位置,显然就是放在树的直径上最优,我们就可以先求出树的直径,然后二分答案,最后取离直径上离端点距离答案的点,遍历 check 一遍。时间复杂度nlogn

#include <bits/stdc++.h>
#define ll long long
#define res register 
#define MAXN 100050
#define int ll 
using namespace std;
int deep[MAXN],head[MAXN<<2],x,y,n,m,maxn1,maxn2,root1,ans1,siz[MAXN],fa[MAXN][20],root2,ans2;
int dl,o,r1,r2,num[200500],now;
struct Node{
    int to,nxt;
}g[MAXN<<4];
inline void dfs(int u,int fa,int d){
    if(d>dl){dl=d,o=u;}
    for(res int i=head[u];i;i=g[i].nxt){
        int to=g[i].to;if(to==fa) continue;
        dfs(to,u,d+1);
    }
}
inline void add(int u,int v){
    static int top=0;
    g[++top].to=v;g[top].nxt=head[u];
    head[u]=top;
}
inline void mark(int u,int fa,int qwq){
    if(!qwq||qwq<=num[u]) return ;
    if(!num[u]) now++;
    num[u]=qwq;
    for(res int i=head[u];i;i=g[i].nxt){
        int to=g[i].to;if(to==fa) continue;
        mark(to,u,qwq-1);
    }
}
inline bool check(int x){
    now=0;
    memset(num,0,sizeof(num));
    mark(r1,0,x<<1|1);
    mark(r2,0,x<<1|1);
    return now==n;
}
inline int erfen(int l,int r){
    if(l==r) return l;
    int mid=l+r>>1;
    if(check(mid)) return erfen(l,mid);
    else return erfen(mid+1,r);
}
ll read()
{
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();}
    return s*w;
}
signed main()
{
    n=read();
    for(res int i=1;i<n;i++){
        x=read(),y=read();
        add(x,y);add(y,x);
    }
    dfs(1,0,1);r1=o;dl=0;
    dfs(r1,0,1);r2=o;
    cout<<erfen(0,1e6)<<endl;
    return 0;
}
View Code

 

总结,对于在树上任意选几个点,使得每个点到这几个选中的点的距离最大值最小,是标准的二分模型,我们在考虑奖杯放置的位置,其实有讲究,是一定在树的直径上更优秀的,然后我们考虑在树的直径上二分答案,就可以优秀的求出答案了

posted @ 2019-11-12 15:17  Lbmttw_lx  阅读(160)  评论(0编辑  收藏  举报