2017 百度之星 初赛B轮 HDU6114 HDU6118 HDU6119

先吐槽一下,百毒之星真不愧为百毒之星啊,这次初赛B轮1002 神TM(1e5)^3也能在10s内跑过,这TM是量子服务器吧,还有资格赛各种数据+题面问题,无力吐槽了都。


传送门:HDU6114

题意:中文题。

思路:仔细分析一下就是求C(n,m),比赛的时候因为数据量小不想处理逆元xjb写了半天都不对,最后还是从博客里扒了个lucas的板子交了。

代码:

#include
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair 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[n] * 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
#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)
{
    queueq;
    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
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair 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
*/


你可能感兴趣的:(hdu,网络流,尺取法,组合数学)