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).
只要可以找到这样的位置,就可以找到一个满足题意的正数...