[BZOJ 1070][SCOI2007]修车
啊啊啊我为什么要去颓费用流啊……
这个题可以保证每个人的车最后都会被修,所以说也就是让你最小化总等待时间辣!
直接考虑给一个技术人员建时间轴是麻烦而不可取的。我们考虑一点,对于每一个技术人员,若总共修了\(t\)辆车,那么第\(i\)个来找他修车的人会影响后面人的等待时间,换言之我们可以认为第\(i\)个来修车的人给答案做出了\(c\cdot (t - i + 1)\)(这里用\(c\)表示他自己的修车时间,注意他也会影响他自己)的贡献,似乎可以苟最小费用最大流呢。
但问题在于我们并不能提前知道\(t\)是多少,并且这样的话费用不是递增的,一个人可能会优先考虑到后面的位置去修车。那么我们考虑倒过来,去考虑倒数第\(i\)个来修车的人的贡献,显然这个贡献是\(c\cdot i\)。然后接下来就肥肠简单了,,,
代码:
/************************************************************** Problem: 1070 User: danihao123 Language: C++ Result: Accepted Time:576 ms Memory:13928 kb ****************************************************************/ #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <utility> #include <algorithm> #include <queue> #include <cassert> typedef long long ll; int m, n; const int maxno = 100005; const int maxe = 400005; 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 = 0LL) { #ifdef LOCAL printf("Inserting Edge (%d, %d, %d, %lld)\n", u, v, f, c); #endif add_edge(u, v, f, c); add_edge(v, u, 0, -c); } const ll LINF = 0x7f7f7f7f7f7f7f7fLL; int tot; 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 + tot + 1, LINF); std::fill(inq, inq + tot + 1, false); std::fill(a, a + tot + 1, 0); std::fill(p, p + tot + 1, 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)); } int pos[105][105][2]; int main() { scanf("%d%d", &m, &n); int s = 0, t = 1; tot = 1; for(int i = 1; i <= n; i ++) { ins_edge(s, ++ tot, 1); } for(int i = 1; i <= m; i ++) { for(int j = 1; j <= n; j ++) { pos[i][j][0] = ++ tot; pos[i][j][1] = ++ tot; ins_edge(tot - 1, tot, 1); ins_edge(tot, t, 1); } } for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m; j ++) { int T; scanf("%d", &T); for(int k = 1; k <= n; k ++) { int id = pos[j][k][0]; int id2 = pos[j][k][1]; ins_edge(i + 1, id, 1, k * T); } } } int f = 0; ll c = 0; mcmf(s, t, f, c); #ifdef LOCAL printf("%d\n", f); #endif printf("%.2lf\n", (double(c)) / (double(n))); return 0; }
[BZOJ 1857][SCOI2010]传送带
三分套三分入门题……
策略肯定是从A走到AB上一点,然后再走到CD上的一个点,再向D走。
很显然答案函数是一个关于那两个点下凸的东西(不会证?GeoGebra之类的东西画一下就好啦!还不如像我这样口胡),所以我们可以先对第一维三分,然后套上对第二维的三分……
代码:
/************************************************************** Problem: 1857 User: danihao123 Language: C++ Result: Accepted Time:244 ms Memory:820 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> #include <utility> #include <cmath> typedef double R; const R eps = 1e-6; int sign(R x) { if(fabs(x) < eps) { return 0; } else { if(x < 0) return -1; else return 1; } } struct Point { R x, y; Point(R qx = 0, R qy = 0) { x = qx; y = qy; } }; typedef Point Vector; Vector operator +(const Vector &a, const Vector &b) { return Vector(a.x + b.x, a.y + b.y); } Vector operator -(const Point &a, const Point &b) { return Vector(a.x - b.x, a.y - b.y); } Vector operator *(R x, const Vector &v) { return Point(v.x * x, v.y * x); } Vector operator *(const Vector &v, R x) { return Point(v.x * x, v.y * x); } R dot(const Vector &a, const Vector &b) { return a.x * b.x + a.y * b.y; } R times(const Vector &a, const Vector &b) { return a.x * b.y - a.y * b.x; } R dist(const Point &a, const Point &b) { return sqrt(dot(a - b, a - b)); } bool cmp(const Point &a, const Point &b) { if(sign(a.x - b.x) == 0) { return a.y < b.y; } else { return a.x < b.x; } } Point A, B, C, D; R p, q, r; Vector D_AB, D_DC; R f(const Point &AB, const Point &CD) { return (dist(AB, A) / p + dist(CD, D) / q + dist(AB, CD) / r); } R F(Point AB) { R L = 0, R = 1; int T = 200; while(T --) { double M1 = L + (R - L) / 3; double M2 = R - (R - L) / 3; Point P1 = D + M1 * D_DC; Point P2 = D + M2 * D_DC; double f1 = f(AB, P1), f2 = f(AB, P2); if(f1 < f2) { R = M2; } else { L = M1; } } return f(AB, D + L * D_DC); } R solve() { R L = 0, R = 1; int T = 200; while(T --) { double M1 = L + (R - L) / 3; double M2 = R - (R - L) / 3; Point P1 = A + M1 * D_AB; Point P2 = A + M2 * D_AB; double F1 = F(P1), F2 = F(P2); if(F1 < F2) { R = M2; } else { L = M1; } } return F(A + L * D_AB); } int main() { scanf("%lf%lf%lf%lf", &A.x, &A.y, &B.x, &B.y); scanf("%lf%lf%lf%lf", &C.x, &C.y, &D.x, &D.y); scanf("%lf%lf%lf", &p, &q, &r); D_AB = B - A; D_DC = C - D; printf("%.2lf\n", solve()); return 0; }
[BZOJ 1086][SCOI2005]王室联邦
最近想捉一下树分块,这道题肯定是要做的啦~
这个题的方法就是蜜汁贪心,在其他树分块的题目中也有应用。
代码:
/************************************************************** Problem: 1086 User: danihao123 Language: C++ Result: Accepted Time:16 ms Memory:880 kb ****************************************************************/ #include <cstdio> #include <stack> using std::stack; const int maxn=1005; const int maxm=maxn*2; int first[maxn]; int next[maxm],to[maxm]; int graph_cnt=0; inline void AddEdge(int u,int v){ graph_cnt++; next[graph_cnt]=first[u]; first[u]=graph_cnt; to[graph_cnt]=v; } int belong[maxn],cap[maxn]; int block_cnt=0; stack<int> S; int B; void DFS(int u,int fa){ int siz=S.size(); for(int i=first[u];i;i=next[i]){ int v=to[i]; if(v==fa){ continue; } DFS(v,u); if((S.size()-siz)>=B){ block_cnt++; cap[block_cnt]=u; while(S.size()>siz){ int x=S.top();S.pop(); belong[x]=block_cnt; } } } S.push(u); } int main(){ register int i; bool OK; int n,u,v; scanf("%d%d",&n,&B); for(i=1;i<=(n-1);i++){ scanf("%d%d",&u,&v); AddEdge(u,v); AddEdge(v,u); } if(n<B){ puts("0"); return 0; } DFS(1,0); while(!S.empty()){ int x=S.top();S.pop(); belong[x]=block_cnt; } printf("%d\n",block_cnt); OK=false; for(i=1;i<=n;i++){ if(OK){ putchar(' '); } OK=true; printf("%d",belong[i]); } putchar('\n'); OK=false; for(i=1;i<=block_cnt;i++){ if(OK){ putchar(' '); } OK=true; printf("%d",cap[i]); } putchar('\n'); return 0; }
[BZOJ 2330]糖果
比较裸一些的差分约束系统题……话说好像第一次写SPFA最长路啊TAT
这个题1,3,5条件直接搞就可以。2,4条件要转化成有等于号的版本。
顺便说一些这题的坑点:
- 有一个点是一个十万个点的链。在从源点连的时候,如果正序连就会炸。倒着连就不会T。
- 有的点的2,4条件出现了[tex]a=b[/tex]的情况,要特判……
- 要开long long!
代码:
/************************************************************** Problem: 2330 User: danihao123 Language: C++ Result: Accepted Time:356 ms Memory:7592 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; #define REP(i,n) for(i=1;i<=(n);i++) #define GRAPH_REP(i,u) for(i=first[(u)];i;i=next[i]) #define CL_ARR(i,x) memset(i,x,sizeof(i)) const int maxn=100005,maxm=300005; typedef long long ll; int first[maxn]; int next[maxm],to[maxm]; ll dist[maxm]; int graph_cnt=0; inline void AddEdge(int u,int v,ll d){ graph_cnt++; next[graph_cnt]=first[u]; first[u]=graph_cnt; to[graph_cnt]=v; dist[graph_cnt]=d; } int n; bool inQueue[maxn]; ll d[maxn]; int cnt[maxn]; ll SPFA(){ register int i,u; register ll ans=0; queue<int> Q; CL_ARR(d,-1); d[0]=0; Q.push(0); inQueue[0]=true; while(!Q.empty()){ u=Q.front(); Q.pop(); inQueue[u]=false; GRAPH_REP(i,u){ if(d[u]>-1 && d[to[i]]<d[u]+dist[i]){ d[to[i]]=d[u]+dist[i]; if(!inQueue[to[i]]){ Q.push(to[i]); inQueue[to[i]]=true; if((++cnt[to[i]])>(n+1)) return -1; } } } } REP(i,n){ ans+=d[i]; } return ans; } int main(){ register int i; int opt,u,v; int k; scanf("%d%d",&n,&k); REP(i,k){ scanf("%d%d%d",&opt,&u,&v); if(opt==1){ AddEdge(u,v,0); AddEdge(v,u,0); }else{ if(opt==2){ if(u==v){ puts("-1"); return 0; } AddEdge(u,v,1); }else{ if(opt==3){ AddEdge(v,u,0); }else{ if(opt==4){ if(u==v){ puts("-1"); } AddEdge(v,u,1); }else{ AddEdge(u,v,0); } } } } } for(i=n;i>=1;i--){ AddEdge(0,i,1); } printf("%lld\n",SPFA()); return 0; }