2017 百度之星 初赛B轮 HDU6114 HDU6118 HDU6119
2017-08-13 18:56
253 查看
先吐槽一下,百毒之星真不愧为百毒之星啊,这次初赛B轮1002 神TM(1e5)^3也能在10s内跑过,这TM是量子服务器吧,还有资格赛各种数据+题面问题,无力吐槽了都。
传送门:HDU6114
题意:中文题。
思路:仔细分析一下就是求C(n,m),比赛的时候因为数据量小不想处理逆元xjb写了半天都不对,最后还是从博客里扒了个lucas的板子交了。
代码:
传送门:HDU6118
题意:中文题。
思路:这题真的是让我哭笑不得,读完题感觉是费用流,写完结果连样例都过不了,才发现是题意读错了,然后一顿xjb debug,然后就收到两发wa,这时忽然发现了错误样例(贴在代码后面了),然后发觉这好像是个没有流量限制的费用流,也就是说并不需要是最大流,只需要费用最小就行,想起白书上好像提到过,翻了翻找到了,不过书上提供的思路是按书上的算法代码改进的,我是用的我原来的最小费用最大流模板,并不知道该怎么改。。
这时候只剩不到十分钟了,再对着书敲也来不及了,然后我就想通过加边使正权增广的部分的费用给去掉,结果还真把自己的用例给过了,不报任何希望的交了一发,赛后突然发现竟然TM
。。。
虽然A了,但是我感觉我是卡过数据的,毕竟百毒之星。。
下面说点正经的:
建图:
1.首先要拆点,目的是限制每个点的最大销售量,最大生产量可以通过源点到每个点的边的容量去限制。
2.注意边权的限制,由于求最大获利,因此要把花费设成正边权,收益设成负边权。
3.重要的一点就是由于是最小费用最大流模板,因此一定会得到最大流,这样就会出现正权增广的情况,然而根据题意我们是不需要这部分的,因此对于每个点要额外加一条0权边使得“多余的”流量不会产生正权。
PS:我看HDU上人家的代码都跑的好快呀,忽然感觉正解可能不是费用流的样子。。
8/14更:
请忽略掉以上的傻逼分析!直接看这里!由于不要求得到最大流,因此我们直接在增广部分判断是不是正权增广路就行!也就是说dis[t] >= 0 的时候直接break!!! 昨天好TM 傻X。。不过建图部分还是一样的。。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int MAXN=1110;
const int MAXM=50010;
int head[MAXN],pre[MAXN],dis[MAXN],book[MAXN];
int cnt=0,N;//N为点数
struct node{
int to,next,cap,flow,cost;
}edge[MAXM];
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
edge[cnt].to=v;edge[cnt].cap=cap;edge[cnt].flow=0;
edge[cnt].cost=cost;edge[cnt].next=head[u];head[u]=cnt++;
edge[cnt].to=u;edge[cnt].cap=0;edge[cnt].flow=0;
edge[cnt].cost=-cost;edge[cnt].next=head[v];head[v]=cnt++;
}
bool spfa(int s,int t)
{
queue<int>q;
while(!q.empty())q.pop();
for(int i=0;i<=N;i++)
{
dis[i]=inf;
pre[i]=-1;
book[i]=0;
}
dis[s]=0;book[s]=1;q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();book[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost)
{
dis[v]=dis[u]+edge[i].cost;
pre[v]=i;
if(!book[v])
{
book[v]=1;
q.push(v);
}
}
}
}
return pre[t]!=-1;
}
int min_cost_max_flow(int s,int t,ll &cost)
{
int flow=0;
cost=0;
while(spfa(s,t))
{
if(dis[t] >= 0) break; // 这句话保证了不一定要是最大流,只要费用最小就好。
int temp=inf;
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])
temp=min(temp,edge[i].cap-edge[i].flow);
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])
{
edge[i].flow+=temp;
edge[i^1].flow-=temp;
cost+=edge[i].cost*temp;
}
flow+=temp;
}
return flow;
}
int a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int main()
{
int n, m;
while(cin >> n >> m)
{
int u, v, w, k;
init();
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
memset(d, 0, sizeof(d));
N = 2 * n + 2;
int source = 0, sink = 2 * n + 1;
for(int i = 1; i <= n; i++)
{
scanf("%d %d %d %d", &a[i], &b[i], &c[i], &d[i]);
}
for(int i = 1; i <= m; i++)
{
scanf("%d %d %d", &u, &v, &k);
addedge(u, v, inf, k);
addedge(v, u, inf, k);
}
for(int i = 1; i <= n; i++)
{
addedge(source, i, b[i], a[i]);
if(d[i])
addedge(i, i + n, d[i], -c[i]);
addedge(i + n, sink, inf, 0);
//下面是比赛时候的傻X想法
//addedge(i, sink, b[i], -a[i]);//上面说的关键点就是这里,而且注意容量不能设置成inf,不然从其他点(非源点)
}//来的流量也会从这里流掉形成增广路,别问我怎么知道的。。
ll cost = 0;
min_cost_max_flow(source, sink, cost);
cout << -cost << endl;
}
}
/*
2 1
5 5 6 1
3 5 7 7
1 2 1
ans:
23
2 1
5 5 3 5
1 5 2 5
1 2 10
ans:
5
*/
传送门:HDU6119
题意:中文题。
思路:区间问题,我是用尺取做的,好像直接贪心也可以。
代码:
传送门:HDU6114
题意:中文题。
思路:仔细分析一下就是求C(n,m),比赛的时候因为数据量小不想处理逆元xjb写了半天都不对,最后还是从博客里扒了个lucas的板子交了。
代码:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; typedef pair<int,int> P; const int MAXN = 1010; const int mod = 1e9 + 7; ll inv[MAXN + 10], fac[MAXN + 10]; void init() { inv[0] = fac[0] = inv[1] = fac[1] = 1; for(int i = 1; i < MAXN; i++) fac[i] = fac[i - 1] * i % mod; for(int i = 2; i < MAXN; i++) inv[i] = (mod - (mod / i)) * inv[mod % i] % mod;//lucas定理求逆元 for(int i = 1; i < MAXN; i++) inv[i] = inv[i - 1] * inv[i] % mod; } ll C(int n, int m)//求组合数 C n,m { return (fac * inv[m] % mod) * inv[n - m] %mod; } int main() { int T; cin >> T; int n, m; init(); while(T--) { cin >> n >> m; if(n < m) swap(n, m); cout << C(n, m) << endl; } return 0; }
传送门:HDU6118
题意:中文题。
思路:这题真的是让我哭笑不得,读完题感觉是费用流,写完结果连样例都过不了,才发现是题意读错了,然后一顿xjb debug,然后就收到两发wa,这时忽然发现了错误样例(贴在代码后面了),然后发觉这好像是个没有流量限制的费用流,也就是说并不需要是最大流,只需要费用最小就行,想起白书上好像提到过,翻了翻找到了,不过书上提供的思路是按书上的算法代码改进的,我是用的我原来的最小费用最大流模板,并不知道该怎么改。。
这时候只剩不到十分钟了,再对着书敲也来不及了,然后我就想通过加边使正权增广的部分的费用给去掉,结果还真把自己的用例给过了,不报任何希望的交了一发,赛后突然发现竟然TM
。。。
虽然A了,但是我感觉我是卡过数据的,毕竟百毒之星。。
下面说点正经的:
建图:
1.首先要拆点,目的是限制每个点的最大销售量,最大生产量可以通过源点到每个点的边的容量去限制。
2.注意边权的限制,由于求最大获利,因此要把花费设成正边权,收益设成负边权。
3.重要的一点就是由于是最小费用最大流模板,因此一定会得到最大流,这样就会出现正权增广的情况,然而根据题意我们是不需要这部分的,因此对于每个点要额外加一条0权边使得“多余的”流量不会产生正权。
PS:我看HDU上人家的代码都跑的好快呀,忽然感觉正解可能不是费用流的样子。。
8/14更:
请忽略掉以上的傻逼分析!直接看这里!由于不要求得到最大流,因此我们直接在增广部分判断是不是正权增广路就行!也就是说dis[t] >= 0 的时候直接break!!! 昨天好TM 傻X。。不过建图部分还是一样的。。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int MAXN=1110;
const int MAXM=50010;
int head[MAXN],pre[MAXN],dis[MAXN],book[MAXN];
int cnt=0,N;//N为点数
struct node{
int to,next,cap,flow,cost;
}edge[MAXM];
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
edge[cnt].to=v;edge[cnt].cap=cap;edge[cnt].flow=0;
edge[cnt].cost=cost;edge[cnt].next=head[u];head[u]=cnt++;
edge[cnt].to=u;edge[cnt].cap=0;edge[cnt].flow=0;
edge[cnt].cost=-cost;edge[cnt].next=head[v];head[v]=cnt++;
}
bool spfa(int s,int t)
{
queue<int>q;
while(!q.empty())q.pop();
for(int i=0;i<=N;i++)
{
dis[i]=inf;
pre[i]=-1;
book[i]=0;
}
dis[s]=0;book[s]=1;q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();book[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost)
{
dis[v]=dis[u]+edge[i].cost;
pre[v]=i;
if(!book[v])
{
book[v]=1;
q.push(v);
}
}
}
}
return pre[t]!=-1;
}
int min_cost_max_flow(int s,int t,ll &cost)
{
int flow=0;
cost=0;
while(spfa(s,t))
{
if(dis[t] >= 0) break; // 这句话保证了不一定要是最大流,只要费用最小就好。
int temp=inf;
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])
temp=min(temp,edge[i].cap-edge[i].flow);
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])
{
edge[i].flow+=temp;
edge[i^1].flow-=temp;
cost+=edge[i].cost*temp;
}
flow+=temp;
}
return flow;
}
int a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int main()
{
int n, m;
while(cin >> n >> m)
{
int u, v, w, k;
init();
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
memset(d, 0, sizeof(d));
N = 2 * n + 2;
int source = 0, sink = 2 * n + 1;
for(int i = 1; i <= n; i++)
{
scanf("%d %d %d %d", &a[i], &b[i], &c[i], &d[i]);
}
for(int i = 1; i <= m; i++)
{
scanf("%d %d %d", &u, &v, &k);
addedge(u, v, inf, k);
addedge(v, u, inf, k);
}
for(int i = 1; i <= n; i++)
{
addedge(source, i, b[i], a[i]);
if(d[i])
addedge(i, i + n, d[i], -c[i]);
addedge(i + n, sink, inf, 0);
//下面是比赛时候的傻X想法
//addedge(i, sink, b[i], -a[i]);//上面说的关键点就是这里,而且注意容量不能设置成inf,不然从其他点(非源点)
}//来的流量也会从这里流掉形成增广路,别问我怎么知道的。。
ll cost = 0;
min_cost_max_flow(source, sink, cost);
cout << -cost << endl;
}
}
/*
2 1
5 5 6 1
3 5 7 7
1 2 1
ans:
23
2 1
5 5 3 5
1 5 2 5
1 2 10
ans:
5
*/
传送门:HDU6119
题意:中文题。
思路:区间问题,我是用尺取做的,好像直接贪心也可以。
代码:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; typedef pair<int,int> P; const int MAXN = 100010; P p[MAXN]; int main() { int n, m; while(cin >> n >> m) { for(int i = 0; i < n; i++) scanf("%d %d", &p[i].first, &p[i].second); sort(p, p + n); int cnt = 0; for(int i = 0; i < n; i++)//将所有区间处理成不相交区间 { int l = i, r = i + 1, last = p[i].second; while(r < n && p[r].first <= last) { last = max(last, p[r].second); r++; } r--; p[cnt++] = P(p[l].first, last); i = r; } n = cnt; //cout << n << endl; int l, r; l = r = 0; int tmp = m; int ans = m; while(r < n)//尺取 { while(r + 1 < n && p[r + 1].first - p[r].second - 1 <= tmp) { tmp -= (p[r + 1].first - p[r].second - 1); r++; } ans = max(ans, p[r].second - p[l].first + tmp + 1); if(l + 1 < n && (p[l + 1].first - p[l].second - 1) > 0 && (p[l + 1].first - p[l].second - 1) + tmp <= m) tmp += (p[l + 1].first - p[l].second - 1); l++; if(l > r) r++; } cout << ans << endl; } return 0; } /* 3 10 1 3 2 6 17 19 3 3 1 3 8 9 15 20 1 1 1 0 */
相关文章推荐
- 2017百度之星初赛(B)1006小小粉丝度度熊------hdu6119
- 百度之星 2017初赛第一场 1005 今夕何夕
- 2017百度之星初赛:A-1006. 度度熊的01世界(DFS)
- 2017百度之星初赛(A)1001 小C的倍数问题(求因子数)
- 百度之星2017初赛A-1006-度度熊的01世界
- 2017百度之星初赛(A) 1001 小C的倍数问题(余数定理)
- (最小费用流)hdu 6118(2017百度之星初赛B 1005) 度度熊的交易计划
- 百度之星2017初赛题解(A)
- 2017百度之星初赛(B) 1006 小小粉丝度度熊(区间合并+尺取法)
- 2017百度之星初赛A-1006(HDU-6113)
- 【2017"百度之星"程序设计大赛 - 初赛(B)】度度熊的交易计划
- 2017百度之星初赛
- 2017百度之星初赛(A) 度度熊的01世界(BFS/DFS)
- 2017百度之星初赛A-1006(HDU-6113)
- 百度之星 2017初赛第一场 1001 小C的倍数问题
- 2017百度之星初赛B场第一题Chess--简单杨辉三角问题
- 百度之星2017初赛B1006 小小粉丝度度熊
- 百度之星2017初赛A轮 1001 小C的倍数问题
- 2017百度之星初赛a
- Hdu6118 度度熊的交易计划(2017"百度之星"程序设计大赛 - 初赛(B))