2019.5.25考试总结

题目

考试时忐忑的心理历程

(8:00 到11:30)

》》》》》》》》》》》》》8:32
第一题,搜索:
枚举每种药材选和不选,然后复杂度约为: 2 20 = 1048576 2^{20} =1048576 220=1048576,还可以欸,但为了更快,可以用一个位运算哈希一下便于判断,哈希最大值为 2 50 2^{50} 250,要炸。。
第二题,图论(floyd或tarjan吧)
第三题,树形DP
》》》》》》》》》》》》》9:22
第一题大样例凉了
》》》》》》》》》》》》》9:52
第一题弃疗
第二题tarjan缩点后统计每个人的所属联通分量的点数是否大于1,如果是,T,否,F,假设它时间复杂度过得去吧
》》》》》》》》》》》》》10:12
第二题大数据过了
》》》》》》》》》》》》》10:24
第一题大数据过了
》》》》》》》》》》》》》10:42
第三题,kruskal建一个新图,然后乱搞应该可以拿到50pts,有希望拿到60pts,因为kruskal的复杂度为 O ( l o g M ) O(logM) O(logM),建图为 O ( m ) O(m) O(m),乱搞(dfs)为 O ( N 2 ) O({N}^{2}) O(N2) , 总复杂度为 N 2 {N}^{2} N2,这是紧的上限,大概可以吧,但是正确性堪忧。
》》》》》》》》》》》》》11:08
kruskal的正确性是伪的,只好乱搞了
》》》》》》》》》》》》》11:15
乱搞搞出来了,就是大样例呵呵呵
》》》》》》》》》》》》》11:30
做了点小优化,但是答案好像超过long long维护不了,就这样了吧

最后得分:210,预计得分:240
问题:审题缺乏精确度,模版不熟练(kruskal),心态波动太大

题解

T1

第一眼就看出是个搜索,但读漏了条件:药材中可能有两个相同的元素,导致我调了一个小时~~~~
搜索策略也很easy啊,见代码吧:

#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define ll long long
template<typename T>void read(T &x){
    x=0;char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}
int n,m;
const int maxn=20+10,maxm=50+10;
bool aim[maxm];
bool che[maxn][maxm];
bool sit[maxm];
inline void datasetting(){
    clean(aim,0),clean(che,0);clean(sit,0);
    register int pos,pos2;
    loop(i,1,m){
        read(pos);
        aim[pos]=true;
    }loop(i,1,n){
        read(pos);
        loop(j,1,pos){
            read(pos2);
            if(che[i][pos2]==false)che[i][pos2]=true;
            else che[i][pos2]=false;
        }
    }
}
bool dfs(int pos){
    if(pos>n){
        loop(i,1,50)
            if(sit[i]==0&&aim[i]==1)return false;
        return true;
    }
    loop(i,pos,n){
        if(dfs(pos+1))return true;
        else{
            loop(j,1,50)sit[j]=sit[j]^che[i][j];
            if(dfs(pos+1))return true;
            else {loop(j,1,50)sit[j]=sit[j]^che[i][j];return false;}
        }
    }
}
int main(){
    freopen("medicine.in","r",stdin); 
    freopen("medicine.out","w",stdout);
    while(scanf("%d%d",&n,&m)!=EOF){
        datasetting();
        if(dfs(1))printf("Possible\n");
        else printf("Impossible\n");
    }
    return 0;
}
/**************************************************************
    Problem: 1474
    User: mzg1802
    Language: C++
    Result: 正确
    Time:640 ms
    Memory:1716 kb
****************************************************************/

T2

这道题算是瞎猫撞上死耗子了,正在我练tarjan的时候出这种题???
这道题是缩点的经典套路,20min解决

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define ll long long
template<typename T>void read(T &x){
    x=0;char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}
int n,m,cnt=0;
const int maxn=100000+10,maxm=200000+10;
struct node{int _v;int nxt;}edge[maxm];
int head[maxn];
inline void addl(int u,int v){
    edge[cnt]._v=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt++;
} 
int dfn[maxn],low[maxn],belong[maxn],num=0,col=0;
int sta[maxn],top=0;
int siz[maxn];
void tarjan(int u){
    dfn[u]=low[u]=++num;sta[++top]=u;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i]._v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(!belong[v])low[u]=min(low[u],dfn[v]);
    }if(dfn[u]==low[u]){
        belong[u]=++col;
        ++siz[col];
        while(top>=0&&sta[top]!=u){
            belong[sta[top]]=col;
            ++siz[col];
            --top;
        }
        --top;
    }
}
int main(){
    freopen("message.in","r",stdin);
    freopen("message.out","w",stdout);
    read(n),read(m);clean(sta,0),clean(siz,0);
    clean(head,-1);clean(low,0),clean(dfn,0);clean(belong,0);
    register int xi,yi;
    loop(i,1,m){
        read(xi),read(yi);
        addl(xi,yi);
    }
    loop(i,1,n)
        if(!dfn[i])
            tarjan(i);
    loop(i,1,n){
        if(siz[belong[i]]>1)printf("T\n");
        else printf("F\n");
    }
    return 0;
}
 
/**************************************************************
    Problem: 1475
    User: mzg1802
    Language: C++
    Result: 正确
    Time:68 ms
    Memory:6740 kb
****************************************************************/

T3

这道题我确实没想出来正解,但是下来后几个巨佬都说这是伟大的奶牛聚集加上一个最小生成树。
后来把这个坑补了,发现这个东西很有趣,主要有趣的地方在树的重心
重心:

对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数(或节点权值和)最小。换句话说,删除这个点后最大连通块(一定是树)的结点数最小。

从定义出发我们可以推出如下性质:

  • 删除重心后最大连通块的结点数K最小, K &lt; = ∑ i = 1 n P [ i ] 2 K&lt;=\frac{\sum_{i=1}^{n} P[i]}{2} K<=2i=1nP[i]
  • 树的重心的位置和边权无关
  • 树中所有点到某个点的距离和中,到重心的距离和是最小的
    (距离和 T= ∑ i = 1 n d i s [ i ] × P [ i ] \sum_{i=1}^{n}dis[i]\times P[i] i=1ndis[i]×P[i],dis[i]是第i个点到重心的距离,P[i]是这个点的权值)

第一个结论是显然的,反证法可证
至于第2和3个结论的证明,我们可以借鉴带权中位数的证法,即调整法:
在这里插入图片描述

假设当前已经找到有一个重心设为u(如图),然后我们考虑将重心u经过边u->v向其四周的点移动,探究T的大小变化。若将重心由u移到v,设W为u,v连边的边权,则
T的增加量 △ A = ( P [ a ] + P [ b ] + P [ c ] ) × W \triangle A=(P[a]+P[b]+P[c])\times W A=(P[a]+P[b]+P[c])×W
T的减少量 △ B = P [ d ] × W \triangle B=P[d]\times W B=P[d]×W
而根据第一条性质,易有 P [ a ] + P [ b ] + P [ c ] &gt; ∑ i = 1 n P [ i ] 2 &gt; = P [ d ] P[a]+P[b]+P[c]&gt;\frac{\sum_{i=1}^{n} P[i]}{2}&gt;=P[d] P[a]+P[b]+P[c]>2i=1nP[i]>=P[d]
P [ a ] + P [ b ] + P [ c ] &gt; P [ d ] P[a]+P[b]+P[c]&gt;P[d] P[a]+P[b]+P[c]>P[d]
等式两边同时乘上 W W W
△ A &gt; △ B \triangle A&gt;\triangle B A>B,即移动重心获得的T减少量少于增加量,故重心的距离和T最小,得证

而这道题还有一个细节,就是为什么kuskal后就可以满足最大边最小了呢?即为什么最小生成树的最大边权为生成树中最大边权最小的?我考试的时候用的是显然成立法,但其实很简单,可以用反证法证出来,

即:假如最小生成树(设为K)里面的最大边 l 1 l1 l1不是所有方案中最小的,其两端端点为u,v,那么必定有另一生成树(设为T)的最大边 l 2 l2 l2长度小于 l 1 l1 l1,则在T中,连接u,v两点的边 l 3 l3 l3的长度必定小于在K中的 l 1 l1 l1的长度( l 1 &gt; l 2 &gt; l 3 l1&gt;l2&gt;l3 l1>l2>l3),与K为最小生成树的题设条件矛盾,得证

故本题确实是一个MST加一个树的重心就可以解决的问题,没有做出来是因为我对最小生成树的性质不熟悉和对重心不熟悉,总之一句话,还是太弱了,唉~~~~

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;i)
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define ll long long
template<typename T>void read(T &x){
    x=0;char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}
int n,m,cnt=0;
const int maxn=1e5+10,maxm=2e5+10;
struct node{int _v;int _w;int nxt;}edge[maxm<<1];
int head[maxn];
int P[maxn];
ll sum=0;
inline void addl(int u,int v,int w){
    edge[cnt]._v=v;
    edge[cnt]._w=w;
    edge[cnt].nxt=head[u];
    head[u]=cnt++;
}
ll f[maxn];int h=INT_MAX;
void dfs(int u,int fa){
    f[u]=P[u];ll maxx=0;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i]._v;
        if(v==fa)continue;
        dfs(v,u);
        f[u]+=f[v];
        maxx=max(maxx,f[v]);
    }if(maxx<(sum>>1)&&sum-f[u]<(sum>>1))h=min(h,u);
}
struct e{int s;int e;int w;}line[maxm];
int _f[maxn];
inline bool cmp(e a,e b){return a.w<b.w;}
int findfa(int u){return _f[u]=((_f[u]==u)?u:findfa(_f[u]));}
inline void buildgraph(){
    sort(line+1,line+1+m,cmp);
    ll _sum=0,_time=0,_cny=0;
    loop(i,1,m){
        int x=line[i].s;
        int y=line[i].e;
        int fx=findfa(x);
        int fy=findfa(y);
        if(fx!=fy){
            addl(x,y,line[i].w);
            addl(y,x,line[i].w);
            _f[fx]=fy;
            _time=max(_time,line[i].w);
            _sum+=line[i].w;++_cny;
        }if(_cny>n-1)break;
    }printf("%lld %lld\n",_sum,_time);
}
int dis[maxn];
ll inner_dfs(int u,int fa){
    ll res=0;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i]._v;
        if(v==fa)continue;
        dis[v]=dis[u]+edge[i]._w;
        res+=dis[v]*(ll)P[v];
        res+=inner_dfs(v,u);
    }return res;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("datain2.txt","r",stdin);
    #endif // ONLINE_JUDGE
    clean(head,-1);
    read(n),read(m);loop(i,1,n)_f[i]=i;
    loop(i,1,n)read(P[i]),sum+=P[i];
    loop(i,1,m)read(line[i].s),read(line[i].e),read(line[i].w);
    buildgraph();
    dfs(1,0);
    clean(dis,0);
    printf("%d %lld\n",h,inner_dfs(h,0));
    return 0;
}
/**************************************************************
    Problem: 1476
    User: mzg1802
    Language: C++
    Result: 正确
    Time:148 ms
    Memory:11208 kb
****************************************************************/

尽吾志也而不能至者,可以无悔矣,其孰能讥之乎? ——王安石

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值