[BZOJ 1449][JSOI2009]球队收益
二次函数费用流的入门题……我真太弱了……
可以给比赛、队伍都建点,然后S向每个比赛连容量为1的边,每个比赛向队伍连容量为1的边,来表示赢家是谁。这样一来一次比赛只有一个队伍赢了。
考虑怎么处理那个二次函数费用。费用函数为f(x,y)=Cx2+Dy2,由于一个队伍的总比赛次数是已知的,所以y不需要,不妨假设一个队伍有t场比赛,则费用函数为f(x)=Cx2+D(t−x)2。
对这个函数做差分:Δf(x)=f(x+1)−f(x)=2Cx−2D(t−x)+C+D,然后这个差分是单调不降的。因此我们对所有差分都从队伍向T连边(费用为这一点的差分),这样一来的话会优先选择费用小的边,保证这个队伍的费用一直合法。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | /************************************************************** Problem: 1449 User: danihao123 Language: C++ Result: Accepted Time:732 ms Memory:1448 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> #include <utility> #include <queue> #include <cassert> typedef long long ll; const int maxn = 5005; const int maxm = 1005; const int maxno = maxn + maxm + 5; const int maxe = (1000 * 2 + 1000 * 3) * 2 + 50; int first[maxno]; int next[maxe], from[maxe], to[maxe], flow[maxe], cap[maxe]; ll cost[maxe]; int gcnt = 0; void add_edge( int u, int v, int f, ll c) { gcnt ++; next[gcnt] = first[u]; first[u] = gcnt; from[gcnt] = u; to[gcnt] = v; cap[gcnt] = f; flow[gcnt] = 0; cost[gcnt] = c; } int rev( int i) { return ((i - 1) ^ 1) + 1; } void ins_edge( int u, int v, int f, ll c) { add_edge(u, v, f, c); add_edge(v, u, 0, -c); } int n, m; const ll LINF = 0x7f7f7f7f7f7f7f7fLL; bool spfa( int s, int t, int &f, ll &c) { static ll d[maxno]; static bool inq[maxno]; static int a[maxno], p[maxno]; std::fill(d, d + n + m + 2, LINF); std::fill(inq, inq + n + m + 2, false ); std::fill(a, a + n + m + 2, 0); std::fill(p, p + n + m + 2, 0); d[s] = 0; std::queue< int > Q; Q.push(s); inq[s] = true ; a[s] = 0x7fffffff; while (!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = false ; for ( int i = first[u]; i; i = next[i]) { if (cap[i] > flow[i]) { int v = to[i]; if (d[v] > d[u] + cost[i]) { d[v] = d[u] + cost[i]; a[v] = std::min(a[u], cap[i] - flow[i]); p[v] = i; if (!inq[v]) { Q.push(v); inq[v] = true ; } } } } } if (a[t] == 0) return false ; f += a[t]; c += (ll(a[t])) * d[t]; for ( int e = p[t]; e; e = p[from[e]]) { flow[e] += a[t]; flow[rev(e)] -= a[t]; } return true ; } void mcmf( int s, int t, int &f, ll &c) { while (spfa(s, t, f, c)); } ll win[maxn], lose[maxn], C[maxn], D[maxn]; ll tot[maxn]; int main() { scanf ( "%d%d" , &n, &m); ll ans = 0; for ( int i = 1; i <= n; i ++) { scanf ( "%lld%lld%lld%lld" , &win[i], &lose[i], &C[i], &D[i]); tot[i] = win[i] + lose[i]; } int s = 0, t = n + m + 1; for ( int i = 1; i <= m; i ++) { int a, b; scanf ( "%d%d" , &a, &b); ins_edge(0, i + n, 1, 0); ins_edge(i + n, a, 1, 0); ins_edge(i + n, b, 1, 0); tot[a] ++; tot[b] ++; } for ( int i = 1; i <= n; i ++) { ans += C[i] * win[i] * win[i] + D[i] * (tot[i] - win[i]) * (tot[i] - win[i]); for (ll j = win[i]; j <= (tot[i] - lose[i] - 1LL); j ++) { ins_edge(i, t, 1, 2LL * C[i] * j - 2LL * D[i] * (tot[i] - j) + C[i] + D[i]); } } int f = 0; mcmf(s, t, f, ans); printf ( "%lld\n" , ans); return 0; } |
[BZOJ 1013]球型空间产生器
不行不能颓了……线代其实刚起步……
注意每个点到球心的距离相同,距离之平方亦相同。然后假设所有半径平方为,那么我们先可以列出两个方程(假设
,不过此时事实上可以列三个,但出于方便讨论的目的便只列两个):
两方程作差,得:
整理,得:
对这种方法加以推广,便可列出个
元一次方程。高斯消元求解即可。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /************************************************************** Problem: 1013 User: danihao123 Language: C++ Result: Accepted Time:4 ms Memory:1296 kb ****************************************************************/ #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; const int maxn=20; int n; double M[maxn][maxn]; double A[maxn][maxn]; inline void Gause(){ register int i,j,k,r; register double f; for (i=1;i<=n;i++){ r=i; for (j=i+1;j<=n;j++) if ( fabs (A[j][i])> fabs (A[r][i])) r=j; if (r!=i) for (j=1;j<=(n+1);j++) swap(A[r][j],A[i][j]); for (k=i+1;k<=n;k++){ f=A[k][i]/A[i][i]; for (j=i;j<=n+1;j++) A[k][j]-=f*A[i][j]; } } for (i=n;i>=1;i--){ for (j=i+1;j<=n;j++) A[i][n+1]-=A[j][n+1]*A[i][j]; A[i][n+1]/=A[i][i]; } } int main(){ register int i,j; bool flag= false ; cin>>n; for (i=1;i<=(n+1);i++) for (j=1;j<=n;j++) cin>>M[i][j]; for (i=1;i<=n;i++) for (j=1;j<=(n+1);j++) A[i][j]=0; for (i=1;i<=n;i++){ for (j=1;j<=n;j++){ A[i][j]=2*(M[i+1][j]-M[i][j]); A[i][n+1]+=(M[i+1][j]-M[i][j])*(M[i+1][j]+M[i][j]); } } Gause(); for (i=1;i<=n;i++){ if (flag) putchar ( ' ' ); flag= true ; printf ( "%.3f" ,A[i][n+1]); } putchar ( '\n' ); return 0; } |
[BZOJ 1029]建筑抢修
废了。
我彻底废了。
就一个贪心水体。按截止时间排序然后遍历,当前花时间能干完就干,不能的话看能不能把更劣的选择换。仅此而已。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /************************************************************** Problem: 1029 User: danihao123 Language: C++ Result: Accepted Time:396 ms Memory:2764 kb ****************************************************************/ #include <cstdio> #include <algorithm> #include <queue> using namespace std; typedef long long ll; #define REP(i,n) for(i=0;i<(n);i++) #define REP_B(i,n) for(i=1;i<=(n);i++) const int maxn=150001; struct Node{ int a,b; bool operator <( const Node& x) const { return b<x.b; } bool operator >( const Node& x) const { return b>x.b; } }; Node T[maxn]; priority_queue<ll> Q; int main(){ int n; register int i,ans=0; register ll cur=0; scanf ( "%d" ,&n); for (i=1;i<=n;i++) scanf ( "%d%d" ,&T[i].a,&T[i].b); sort(T+1,T+1+n); for (i=1;i<=n;i++){ if (cur+T[i].a<=T[i].b){ cur+=T[i].a; Q.push(T[i].a); } else { if (Q.size() && Q.top()>T[i].a){ cur-=Q.top(); Q.pop(); cur+=T[i].a; Q.push(T[i].a); } } } printf ( "%d\n" ,Q.size()); return 0; } |
[BZOJ 1015]星球大战
这道题到现在才A……
其实……离线处理并不像我想的那样变态……
需要注意的是,离线处理过程中,在将被删除的点加入时,联通分量数可能增加(其他一个点都连不了)
代码: