2019/9/2 校内练习赛 考试报告
A.一道图论神题(god.cpp)
题目链接
题意
给定一张无向图,每个点有正点权,删掉一个点的代价为与它相连的所有未被删除的节点的权值和,求删掉所有点的最小代价.
解法
考虑任意两个相连的点\(u\)和\(v\).
设\(u\)点权值为\(a\).
\(v\)点权值为\(b\).
与\(u\)相连的节点的权值和,减去\(v\)点权值,结果为\(c\).
与\(v\)相连的节点的权值和,减去\(u\)点权值,结果为\(d\).
那么,如果先删除\(u\),再删除\(v\),代价为\(c+b+d\).
如果先删除\(v\),再删除\(u\),代价为\(d+a+c\).
那么如果\(a>b\),应该先删除\(u\),再删除\(v\).
否则,应该先删除\(v\),再删除\(u\).
即应该先删除\(u,v\)中权值大的点.
显然该结论可以推广到所有点.
所以贪心策略为由大到小删除权值大的点.
代码
#include<bits/stdc++.h>
const int SIZE=200005;
int x[SIZE],head[SIZE],nex[SIZE],to[SIZE],Tot,Ans[SIZE];
void Link(int u,int v)
{
nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;
nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;
}
struct node
{
int x,pos;
bool operator <(const node &p)const
{
return x>p.x;
}
}Tem[SIZE];
bool Removed[SIZE];
long long Tot_Ans;
int main()
{
freopen("god.in","r",stdin);
freopen("god.out","w",stdout);
int n,m,u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&x[i]);
Tem[i]=(node){x[i],i};
}
std::sort(Tem+1,Tem+1+n);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
Link(u,v);
Ans[u]+=x[v];
Ans[v]+=x[u];
}
for(int o=1;o<=n;o++)
{
u=Tem[o].pos;
for(int i=head[u];i;i=nex[i])
{
v=to[i];
if(Removed[v])continue;
Ans[v]-=x[u];
}
Tot_Ans+=Ans[u];
Removed[u]=1;
}
printf("%lld",Tot_Ans);
return 0;
}
B.位运算(bit.cpp)
题目链接
题意
定义数字\(n\)的价值\(k\)为\(|n|\)各位数字之和.
求满足小于\(n\),价值为\(k+1\),且最大的数.
\(|n| \le 10^{100000}\).
解法
分类讨论.
若\(n\)为负数,从低位向高位找第一个一个不为\(9\)的位置,给这个位置\(+1\)即可.如果所有位上都是\(9\),在最前面补\(1\).
若\(n\)为非负数,考虑寻找这样一个位置:
- 这个位置可以\(-1\)(即这个位置上的数不为\(0\)).
- 这个位置往右的那些位置的数字和,可以\(+2\).
只要可以找到这样的位置,就可以找到一个满足题意的正数...