先吐槽一下,百毒之星真不愧为百毒之星啊,这次初赛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;
}
题意:中文题。
思路:这题真的是让我哭笑不得,读完题感觉是费用流,写完结果连样例都过不了,才发现是题意读错了,然后一顿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
*/
题意:中文题。
思路:区间问题,我是用尺取做的,好像直接贪心也可以。
代码:
#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
*/