[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;
}