BZOJ[2563] 阿狸和桃子的游戏
这个题的正解和之前做过的网络流的文理分科差不多,都是一个把对两个点都有贡献的边的贡献分别加给两个点;
在这个题里,首先考虑阿狸拿走了所有的边权与点权,那么现在桃子的分数为0,阿狸的分数为所有权值的加和,那么考虑桃子选每一个点所造成的贡献,每当桃子选了一个点v,则桃子的分数加上w[v],阿狸的分数减去w[v],于此同时,阿狸失去了所有以v为一个端点的边的权值,所以选v时,桃子将差距缩小了2*w[v]+∑以v为一个端点的边的权值,由于每次只加了一次边权,所以当选了一个边的两端点时,贡献就算了2次,刚好符合游戏规则(两次是同点一样,桃子+w,阿狸-w,所以是2*w);
先求出阿狸的sum,然后每一个点的权值就是2*w[v]+∑以v为一个端点的边的权值,然后sort排序,从大到小选N/2个,要注意因为是轮流选,所以桃子只能隔着选,
Code
1 #include <cmath> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 # define maxn 10010 8 # define max_m 100010 9 using namespace std; 10 typedef long long LL; 11 int n,m; 12 LL val[maxn]; 13 struct node{ 14 int u,v,nxt; LL w; 15 }g[2*max_m]; 16 int adj[maxn],e; 17 void add(int u,int v,LL w){ 18 g[e].u=u; g[e].v=v; g[e].w=w; g[e].nxt=adj[u]; 19 adj[u]=e++; 20 } 21 LL sum; 22 void init(){ 23 scanf("%d%d",&n,&m); 24 for(int i=1;i<=n;i++) scanf("%lld",&val[i]),sum+=val[i]; 25 memset(adj,-1,sizeof(adj)); 26 int x,y; LL z; 27 for(int i=1;i<=m;i++){ 28 scanf("%d%d%lld",&x,&y,&z); 29 add(x,y,z); add(y,x,z); 30 sum+=z; 31 } 32 sum=-sum; 33 } 34 bool cmp(const int a,const int b){return a>b;} 35 void work(){ 36 for(int i=1;i<=n;i++){ 37 val[i]*=2; 38 for(int j=adj[i];j!=-1;j=g[j].nxt){ 39 int v=g[j].v; 40 val[i]+=g[j].w; 41 } 42 } 43 sort(val+1,val+n+1,cmp); 44 for(int i=1;i<=n;i+=2){ 45 sum+=val[i]; 46 } 47 cout<<sum<<endl; 48 } 49 int main(){ 50 // freopen("a.in","r",stdin); 51 init(); 52 work(); 53 }