2
22
2016
0

【bzoj2561】最小生成树

2561: 最小生成树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1211  Solved: 585
[Submit][Status][Discuss]

Description

 给定一个边带正权的连通无向图G=(V,E),其中N=|V|,M=|E|,N个点从1到N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树上?

Input

第一行包含用空格隔开的两个整数,分别为N和M;

接下来M行,每行包含三个正整数u,v和w表示图G存在一条边权为w的边(u,v)。

最后一行包含用空格隔开的三个整数,分别为u,v,和 L;

数据保证图中没有自环。

 

Output

 输出一行一个整数表示最少需要删掉的边的数量。

Sample Input

3 2
3 2 1
1 2 3
1 2 2

Sample Output

1
 

HINT

 

对于20%的数据满足N ≤ 10,M ≤ 20,L ≤ 20;

  对于50%的数据满足N ≤ 300,M ≤ 3000,L ≤ 200;

  对于100%的数据满足N ≤ 20000,M ≤ 200000,L ≤ 20000。

 

Source

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 1000000000
using namespace std;
struct node{int x,y,z;}ed[500010];
int n,m,edgenum,ans,source,sink,vs,x,y,z;
int vet[500010],next[500010],head[500010],maxv[500010],dis[500010],gap[500010];
bool cmp(node x,node y){return x.z<y.z;}
void add(int u,int v,int w)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    maxv[edgenum]=w;
}
int dfs(int u,int aug)
{
    if (u==sink) return aug;
    int mind=vs-1,flow=0;
    for (int e=head[u];e;e=next[e])
    {
        if (maxv[e]<=0) continue;
        int v=vet[e];
        if (dis[u]==dis[v]+1)
        {
            int t=dfs(v,min(aug-flow,maxv[e]));
            maxv[e]-=t;
            if (e%2) maxv[e+1]+=t;else maxv[e-1]+=t;
            flow+=t;
            if (dis[source]>=vs) return flow;
            if (aug==flow) break;
        }
        mind=min(mind,dis[v]);
    }
    if (!flow)
    {
        gap[dis[u]]--;
        if (!gap[dis[u]]) dis[source]=vs;
        dis[u]=mind+1;
        gap[dis[u]]++;
    }
    return flow;
}
int maxflow(int s,int t,int n)
{
    int ans=0;
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    source=s;sink=t;gap[0]=vs=n;
    while (dis[source]<vs) ans+=dfs(source,INF);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].z);
    sort(ed+1,ed+m+1,cmp);
    scanf("%d%d%d",&x,&y,&z);
    for (int i=1;i<=m;i++)
    if (ed[i].z>=z) break;else {add(ed[i].x,ed[i].y,1);add(ed[i].y,ed[i].x,1);}
    ans=maxflow(x,y,n);
    edgenum=0;memset(head,0,sizeof(head));
    for (int i=m;i>=1;i--)
    if (ed[i].z<=z) break;else {add(ed[i].x,ed[i].y,1);add(ed[i].y,ed[i].x,1);}
    ans+=maxflow(x,y,n);
    printf("%d\n",ans);
}
Category: 网络流 | Tags:
2
22
2016
0

【bzoj2879】【Noi2012】美食节

2879: [Noi2012]美食节

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 1313  Solved: 708
[Submit][Status][Discuss]

Description

CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而,尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情。于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短。小M发现,美食节共有n种不同的菜品。每次点餐,每个同学可以选择其中的一个菜品。总共有m个厨师来制作这些菜品。当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师。然后每个厨师就会同时开始做菜。厨师们会按照要求的顺序进行制作,并且每次只能制作一人份。此外,小M还发现了另一件有意思的事情: 虽然这m个厨师都会制作全部的n种菜品,但对于同一菜品,不同厨师的制作时间未必相同。他将菜品用1, 2, ..., n依次编号,厨师用1, 2, ..., m依次编号,将第j个厨师制作第i种菜品的时间记为 ti,j 。小M认为:每个同学的等待时间为所有厨师开始做菜起,到自己那份菜品完成为止的时间总长度。换句话说,如果一个同学点的菜是某个厨师做的第k道菜,则他的等待时间就是这个厨师制作前k道菜的时间之和。而总等待时间为所有同学的等待时间之和。现在,小M找到了所有同学的点菜信息: 有 pi 个同学点了第i种菜品(i=1, 2, ..., n)。他想知道的是最小的总等待时间是多少。
 

Input


 输入文件的第1行包含两个正整数n和m,表示菜品的种数和厨师的数量。 第2行包含n个正整数,其中第i个数为pi,表示点第i种菜品的人数。 接下来有n行,每行包含m个非负整数,这n行中的第i行的第j个数为ti,j,表示第j个厨师制作第i种菜品所需的时间。 输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。

Output


 输出仅一行包含一个整数,为总等待时间的最小值。

Sample Input


3 2
3 1 1
5 7
3 6
8 9

Sample Output


47

【样例说明】
厨师1先制作1份菜品2,再制作2份菜品1。点这3道菜的3个同学的等待时间分别为3,3+5=8,3+5+5=13。
厨师2先制作1份菜品1,再制作1份菜品3。点这2道菜的2个同学的等待时间分别为7,7+9=16。
总等待时间为3+8+13+7+16=47。
虽然菜品1和菜品3由厨师1制作更快,如果这些菜品都由厨师1制作,总等待时间反而更长。如果按上述的做法,将1份菜品1和1份菜品
调整到厨师2制作,这样厨师2不会闲着,总等待时间更短。
可以证明,没有更优的点餐方案。


【数据规模及约定】
对于100%的数据,n <= 40, m <= 100, p <= 800, ti,j <= 1000(其中p = ∑pi,即点菜同学的总人数)。
每组数据的n、m和p值如下:
测试点编号 n m p
1 n = 5 m = 5 p = 10
2 n = 40 m = 1 p = 400
3 n = 40 m = 2 p = 300
4 n = 40 m = 40 p = 40
5 n = 5 m = 40 p = 100
6 n = 10 m = 50 p = 200
7 n = 20 m = 60 p = 400
8 n = 40 m = 80 p = 600
9 n = 40 m = 100 p = 800
10 n = 40 m = 100 p = 800
#include<cstdio>
#define INF 1000000000
using namespace std;
int n,m,tot,edgenum,p,ans;
int vet[5000010],next[5000010],head[5000010],maxv[5000010],cost[5000010];
int dis[500010],flag[500010],q[500010],last[500010],pre[500010],a[110],t[110][110],id[110][1010],Id[110];
void add(int u,int v,int w,int z)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    maxv[edgenum]=w;
    cost[edgenum]=z;
}
int spfa()
{
    for (int i=0;i<=tot;i++){dis[i]=INF;flag[i]=0;}
    int p1=-1,p2=0;
    dis[0]=0;q[0]=0;flag[0]=1;
    while (p1<p2)
    {
        int u=q[++p1];
        flag[u]=0;
        for (int e=head[u];e;e=next[e])
        {
            if (maxv[e]<=0) continue;
            int v=vet[e];
            if (dis[u]+cost[e]<dis[v])
            {
                dis[v]=dis[u]+cost[e];
                last[v]=e;pre[v]=u;
                if (!flag[v]){flag[v]=1;q[++p2]=v;}
            }
        }
    }
    if (dis[tot]==INF) return 0;else return 1;
}
void work()
{
    ans+=dis[tot];
    int u=pre[tot];
    int p1=(u-1)/p+1,p2=(u-1)%p+1;
    for (int i=1;i<=n;i++){add(Id[i],id[p1][p2+1],1,t[p1][i]*(p2+1));add(id[p1][p2+1],Id[i],0,-t[p1][i]*(p2+1));}
    u=tot;
    while (u)
    {
        int e=last[u];
        maxv[e]--;
        if (e%2) maxv[e+1]++;else maxv[e-1]++;
        u=pre[u];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)scanf("%d",&t[j][i]);
    for (int i=1;i<=n;i++) p+=a[i];
    for (int i=1;i<=m;i++)
        for (int j=1;j<=p;j++)id[i][j]=++tot;
    for (int i=1;i<=n;i++) Id[i]=++tot;tot++;
    for (int i=1;i<=n;i++){add(0,Id[i],a[i],0);add(Id[i],0,0,0);}
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){add(Id[i],id[j][1],1,t[j][i]);add(id[j][1],Id[i],0,-t[j][i]);}
    for (int i=1;i<=m;i++)
        for (int j=1;j<=p;j++){add(id[i][j],tot,1,0);add(tot,id[i][j],0,0);}
    while (spfa()) work();
    printf("%d\n",ans);
}
Category: 网络流 | Tags:
2
22
2016
0

【bzoj1070】【SCOI2007】修车

1070: [SCOI2007]修车

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3721  Solved: 1507
[Submit][Status][Discuss]

Description

同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

Input

第一行有两个m,n,表示技术人员数与顾客数。 接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。

Output

最小平均等待时间,答案精确到小数点后2位。

Sample Input

2 2
3 2
1 4

Sample Output

1.50

HINT

数据范围: (2<=M<=9,1<=N<=60), (1<=T<=1000)

#include<cstdio>
#define INF 1000000000
using namespace std;
int n,m,tot,edgenum,ans;
int id[100][100],Id[100],a[100][100];
int vet[500010],next[500010],head[500010],maxv[500010],cost[500010],dis[500010],flag[500010],q[500010],last[500010],pre[500010];
void add(int u,int v,int w,int z)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    maxv[edgenum]=w;
    cost[edgenum]=z;
}
int spfa()
{
    for (int i=0;i<=tot;i++){dis[i]=INF;flag[i]=0;}
    int p1=-1,p2=0;
    q[0]=0;dis[0]=0;flag[0]=1;
    while (p1<p2)
    {
        int u=q[++p1];
        flag[u]=0;
        for (int e=head[u];e;e=next[e])
        {
            if (maxv[e]<=0) continue;
            int v=vet[e];
            if (dis[u]+cost[e]<dis[v])
            {
                dis[v]=dis[u]+cost[e];
                last[v]=e;pre[v]=u;
                if (!flag[v]){flag[v]=1;q[++p2]=v;}
            }
        }
    }
    if (dis[tot]==INF) return 0;else return 1;
}
void work()
{
    ans+=dis[tot];
    int u=tot;
    while (u)
    {
        int e=last[u];
        maxv[e]--;
        if (e%2) maxv[e+1]++;else maxv[e-1]++;
        u=pre[u];
    }
}
int main()
{
    scanf("%d%d",&m,&n);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            scanf("%d",&a[j][i]);
    for (int i=1;i<=m;i++)
        for (int j=1;j<=n;j++)
            id[i][j]=++tot;
    for (int i=1;i<=n;i++)
        Id[i]=++tot;
    tot++;
    for (int i=1;i<=n;i++){add(0,Id[i],1,0);add(Id[i],0,0,0);}
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            for (int k=1;k<=n;k++){add(Id[i],id[j][k],1,a[j][i]*k);add(id[j][k],Id[i],0,-a[j][i]*k);}
    for (int i=1;i<=m;i++)
        for (int j=1;j<=n;j++){add(id[i][j],tot,1,0);add(tot,id[i][j],0,0);}
    while (spfa()) work();
    printf("%.2lf\n",(double)ans/n);
}

Category: 网络流 | Tags:
2
22
2016
0

【bzoj2324】【ZJOI2011】营救皮卡丘

2324: [ZJOI2011]营救皮卡丘

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 1895  Solved: 766
[Submit][Status][Discuss]

Description

皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路。

火箭队一共有N个据点,据点之间存在M条双向道路。据点分别从1N标号。小智一行K人从真新镇出发,营救被困在N号据点的皮卡丘。为了方便起见,我们将真新镇视为0号据点,一开始K个人都在0号点。

由于火箭队的重重布防,要想摧毁K号据点,必须按照顺序先摧毁1K-1号据点,并且,如果K-1号据点没有被摧毁,由于防御的连锁性,小智一行任何一个人进入据点K,都会被发现,并产生严重后果。因此,在K-1号据点被摧毁之前,任何人是不能够经过K号据点的。

为了简化问题,我们忽略战斗环节,小智一行任何一个人经过K号据点即认为K号据点被摧毁。被摧毁的据点依然是可以被经过的。

K个人是可以分头行动的,只要有任何一个人在K-1号据点被摧毁之后,经过K号据点,K号据点就被摧毁了。显然的,只要N号据点被摧毁,皮卡丘就得救了。

野外的道路是不安全的,因此小智一行希望在摧毁N号据点救出皮卡丘的同时,使得K个人所经过的道路的长度总和最少。

请你帮助小智设计一个最佳的营救方案吧!

Input

第一行包含三个正整数NMK。表示一共有N+1个据点,分别从0N编号,以及M条无向边。一开始小智一行共K个人均位于0号点。 

接下来M行,每行三个非负整数,第i行的整数为AiBiLi。表示存在一条从Ai号据点到Bi号据点的长度为Li的道路。

Output

仅包含一个整数S,为营救皮卡丘所需要经过的最小的道路总和。

Sample Input

3 4 2
0 1 1
1 2 1
2 3 100
0 3 1

Sample Output

3
【样例说明】
小智和小霞一起前去营救皮卡丘。在最优方案中,小智先从真新镇前往1号点,接着前往2号据点。当小智成功摧毁2号据点之后,小霞从真新镇出发直接前往3号据点,救出皮卡丘。

HINT

 

对于100%的数据满足N ≤ 150, M ≤ 20 000, 1 ≤ K ≤ 10, Li ≤ 10 000, 保证小智一行一定能够救出皮卡丘。至于为什么K ≤ 10,你可以认为最终在小智的号召下,小智,小霞,小刚,小建,小遥,小胜,小光,艾莉丝,天桐,还有去日本旅游的黑猫警长,一同前去大战火箭队。

hzwer.com/4639.html

#include<cstdio>
#include<algorithm>
#define INF 1000000000
using namespace std;
typedef long long ll;
int n,m,K,edgenum,u,v,w;
int a[210][210],vet[500010],next[500010],head[500010],maxv[500010],cost[500010],flag[500010],q[500010],last[500010],pre[500010];
ll dis[500010],ans;
void add(int u,int v,int w1,int w2)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    maxv[edgenum]=w1;
    cost[edgenum]=w2;
}
void spfa()
{
    for (int i=0;i<=n*2+1;i++) {dis[i]=INF;flag[i]=0;}
    int p1=-1,p2=0;
    q[0]=0;flag[0]=1;dis[0]=0;
    while (p1<p2)
    {
        int u=q[++p1];flag[u]=0;
        for (int e=head[u];e;e=next[e])
        {
            int v=vet[e];
            if (maxv[e]>0&&dis[u]+cost[e]<dis[v])
            {
                dis[v]=dis[u]+cost[e];
                last[v]=e;pre[v]=u;
                if (!flag[v]){flag[v]=1;q[++p2]=v;}
            }
        }
    }
}
void work()
{
    int t=n*2+1;
    while (t)
    {
        int e=last[t];
        maxv[e]--;if (e&1) maxv[e+1]++;else maxv[e-1]++;
        t=pre[t];
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&K);
    for (int i=0;i<=n;i++)
        for (int j=0;j<=n;j++) a[i][j]=INF;
    for (int i=0;i<=n;i++) a[i][i]=0;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        a[u][v]=a[v][u]=min(a[u][v],w);
    }
    for (int k=0;k<=n;k++)
        for (int i=0;i<=n;i++)
            for (int j=0;j<=n;j++)if (k<i||k<j) a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
    for (int i=1;i<=n;i++)if (a[0][i]!=INF){add(0,i,1,a[0][i]);add(i,0,0,-a[0][i]);}
    for (int i=1;i<=n;i++){add(i+n,2*n+1,1,0);add(2*n+1,i+n,0,0);}
    for (int i=1;i<=n;i++){add(i,i+n,1,-INF);add(i+n,i,0,INF);}
    for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++)
        if (a[i][j]!=INF){add(i+n,j,1,a[i][j]);add(j,i+n,0,-a[i][j]);}
    while (K--)
    {
        spfa();
        if (dis[n*2+1]>=0) break;
        ans+=dis[n*2+1];
        work();
    }
    ans+=(ll)INF*n;
    printf("%lld\n",ans);
}
Category: 网络流 | Tags:
2
22
2016
0

【bzoj1927】【Sdoi2010】星际竞速

1927: [Sdoi2010]星际竞速

Time Limit: 20 Sec  Memory Limit: 259 MB
Submit: 1651  Solved: 1006
[Submit][Status][Discuss]

Description

10 年一度的银河系赛车大赛又要开始了。作为全银河最盛大的活动之一, 夺得这个项目的冠军无疑是很多人的梦想,来自杰森座 α星的悠悠也是其中之一。 赛车大赛的赛场由 N 颗行星和M条双向星际航路构成,其中每颗行星都有 一个不同的引力值。大赛要求车手们从一颗与这 N 颗行星之间没有任何航路的 天体出发,访问这 N 颗行星每颗恰好一次,首先完成这一目标的人获得胜利。 由于赛制非常开放,很多人驾驶着千奇百怪的自制赛车来参赛。这次悠悠驾 驶的赛车名为超能电驴,这是一部凝聚了全银河最尖端科技结晶的梦幻赛车。作 为最高科技的产物,超能电驴有两种移动模式:高速航行模式和能力爆发模式。 在高速航行模式下,超能电驴会展开反物质引擎,以数倍于光速的速度沿星际航 路高速航行。在能力爆发模式下,超能电驴脱离时空的束缚,使用超能力进行空 间跳跃——在经过一段时间的定位之后,它能瞬间移动到任意一个行星。 天不遂人愿,在比赛的前一天,超能电驴在一场离子风暴中不幸受损,机能 出现了一些障碍:在使用高速航行模式的时候,只能由每个星球飞往引力比它大 的星球,否则赛车就会发生爆炸。 尽管心爱的赛车出了问题,但是悠悠仍然坚信自己可以取得胜利。他找到了 全银河最聪明的贤者——你,请你为他安排一条比赛的方案,使得他能够用最少 的时间完成比赛。

Input

第一行是两个正整数 N, M。 第二行 N 个数 A1~AN, 其中Ai表示使用能力爆发模式到达行星 i 所需的定位 时间。 接下来 M行,每行 3个正整数ui, vi, wi,表示在编号为 ui和vi的行星之间存 在一条需要航行wi时间的星际航路。 输入数据已经按引力值排序,也就是编号小的行星引力值一定小,且不会有 两颗行星引力值相同。

Output

仅包含一个正整数,表示完成比赛所需的最少时间。

Sample Input

3 3
1 100 100
2 1 10
1 3 1
2 3 1

Sample Output

12

HINT

说明:先使用能力爆发模式到行星 1,花费时间 1。 
然后切换到高速航行模式,航行到行星 2,花费时间10。 
之后继续航行到行星 3完成比赛,花费时间 1。 
虽然看起来从行星 1到行星3再到行星 2更优,但我们却不能那样做,因为
那会导致超能电驴爆炸。 

对于 30%的数据 N≤20,M≤50; 
对于 70%的数据 N≤200,M≤4000; 
对于100%的数据N≤800, M≤15000。输入数据中的任何数都不会超过106
。 
输入数据保证任意两颗行星之间至多存在一条航道,且不会存在某颗行星到
自己的航道。

Source

首先将每个星球裂成两个点,源连第一个点流量为1费用为0,第二个点连汇流量为1费用为0,这样就保证每个点经过且仅经过一次。然后边就从第一个点连向第二个点流量1费用边权,瞬移就从源连向第二个点流量1费用权值。

代码:

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 1000000000
using namespace std;
int n,m,u,v,w,edgenum,ans;
int vet[500010],next[500010],head[500010],maxv[500010],cost[500010],dis[500010],flag[500010],q[500010],last[500010],pre[500010],a[500010];
void add(int u,int v,int w1,int w2)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    maxv[edgenum]=w1;
    cost[edgenum]=w2;
}
void spfa()
{
    for (int i=1;i<=2*n+1;i++) dis[i]=INF;
    dis[0]=0;
    memset(flag,0,sizeof(flag));
    int p1=-1,p2=0;
    q[0]=0;
    flag[0]=1;
    while (p1<p2)
    {
        u=q[++p1];
        flag[u]=0;
        for (int e=head[u];e;e=next[e])
        {
            int v=vet[e];
            if (maxv[e]>0&&dis[u]+cost[e]<dis[v])
            {
                dis[v]=dis[u]+cost[e];
                last[v]=e;
                pre[v]=u;
                if (!flag[v]){flag[v]=1;q[++p2]=v;}
            }
        }
    }
}
void work()
{
    int t=2*n+1;
    while (t!=0)
    {
        maxv[last[t]]--;
        if (last[t]&1) maxv[last[t]+1]++;else maxv[last[t]-1]++;
        t=pre[t];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        if (u>v) swap(u,v);
        add(u,v+n,1,w);add(v+n,u,0,-w);
    }
    for (int i=1;i<=n;i++)
    {
        add(0,i,1,0);add(i,0,0,0);
        add(i+n,n*2+1,1,0);add(n*2+1,i+n,0,0);
    }
    for (int i=1;i<=n;i++){add(0,i+n,1,a[i]);add(i+n,0,0,-a[i]);}
    while (1)
    {
        spfa();
        if (dis[n*2+1]==INF) break;
        ans+=dis[n*2+1];
        work();
    }
    printf("%d\n",ans);
}
Category: 网络流 | Tags:
2
4
2016
0

【bzoj3160】万径人踪灭

3160: 万径人踪灭

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 574  Solved: 332
[Submit][Status][Discuss]

Description

Input

Output

 

 

Sample Input

 

Sample Output

 

HINT

 

 

Source

%%%PoPoQQQ,http://blog.csdn.net/PoPoQQQ/article/details/42193259

代码:

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define mod 1000000007
using namespace std;
double pi=acos(-1);
struct cp{double x,y;};
int n,m,ans,len;
int f[500010],mi[500010];
cp a[500010],b[500010],c[500010],cur[500010];
char s[500010],s1[500010];
cp operator *(cp x,cp y){return (cp){x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y};}
cp operator +(cp x,cp y){return (cp){x.x+y.x,x.y+y.y};}
cp operator -(cp x,cp y){return (cp){x.x-y.x,x.y-y.y};}
void fft(cp *a,int n,int fl)
{
    for (int i=n>>1,j=1;j<n;j++)
    {
        if (i<j) swap(a[i],a[j]);
        int k=n>>1;
        for (;k&i;i^=k,k>>=1);i^=k;
    }
    for (int m=2;m<=n;m<<=1)
    {
        cp w=(cp){cos(2*pi*fl/m),sin(2*pi*fl/m)};
        cur[0]=(cp){1,0};
        for (int i=1;i<=m;i++) cur[i]=cur[i-1]*w;
        for (int i=0;i<n;i+=m)
            for (int j=i;j<i+(m>>1);j++)
            {
                cp u=a[j],v=a[j+(m>>1)]*cur[j-i];
                a[j]=u+v;
                a[j+(m>>1)]=u-v;
            }
    }
}
int manacher()
{
    int ans=0;
    s1[0]='$';len=1;
    for (int i=0;i<n;i++){s1[len++]='#',s1[len++]=s[i];}
    s1[len++]='#';
    int mx=1,id=1;
    for (int i=1;i<len;i++)
    {
        f[i]=min(f[id*2-i],mx-i);
        while (s1[i+f[i]]==s1[i-f[i]]) f[i]++;
        if (f[i]+i>mx){mx=f[i]+i;id=i;}
        ans+=f[i]>>1;ans%=mod;
    }
    return ans;
}
int main()
{
    mi[0]=1;
    for (int i=1;i<=500000;i++) mi[i]=mi[i-1]*2%mod;
    scanf("%s",s);
    n=strlen(s);
    m=1;while (m<=n*2) m<<=1;
    for (int i=0;i<n;i++) if (s[i]=='a') a[i].x=1;
    fft(a,m,1);
    for (int i=0;i<m;i++) b[i]=a[i]*a[i];
    fft(b,m,-1);
    for (int i=0;i<m;i++) b[i].x=(int)(b[i].x/m+0.5);
    for (int i=0;i<m;i++) a[i].x=a[i].y=0;
    for (int i=0;i<n;i++) if (s[i]=='b') a[i].x=1;
    fft(a,m,1);
    for (int i=0;i<m;i++) c[i]=a[i]*a[i];
    fft(c,m,-1);
    for (int i=0;i<m;i++) c[i].x=(int)(c[i].x/m+0.5);
    for (int i=0;i<m;i++) f[i]=((int)b[i].x+(int)c[i].x+1)>>1;
    for (int i=0;i<m;i++) {ans+=(mi[f[i]]+mod-1)%mod;ans%=mod;}
    ans=ans+mod-manacher();ans%=mod;
    printf("%d\n",ans);
}
Category: FFT | Tags:
2
4
2016
0

【bzoj3509】【CodeChef】COUNTARI

3509: [CodeChef] COUNTARI

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 554  Solved: 148
[Submit][Status][Discuss]

Description

给定一个长度为N的数组A[],求有多少对i, j, k(1<=i<j<k<=N)满足A[k]-A[j]=A[j]-A[i]。

Input

第一行一个整数N(N<=10^5)。
接下来一行N个数A[i](A[i]<=30000)。

Output

一行一个整数。

Sample Input

10
3 5 3 6 3 4 10 4 5 2

Sample Output

9

HINT

听说这道题可以被一种神奇的暴力水过去,而且时间还比正解要来的快。。那我感觉可能是数据比较水。

我来讲讲FFT的做法吧。首先分块,我们分三种情况来考虑,第一种,三个数在同一个块内,直接暴力。第二种,两个数在同一个块内,另一个数不在这个块内,还是直接暴力。第三种,没有两个数在一个块内,那么我们枚举中间的数x,它对答案的贡献就是它左边的块与它右边的块中,和为2*x的数对个数。

我们令这个块的左右端点为l,r,我们把0~l-1这些数类似于基数排序放进a数组中,把r+1~n用同样的方法放进b数组中,那么对于x,它对答案的贡献即为,sigma(a[i]*b[2*x-i]),发现这就是个卷积的形式,用FFT来解决。

至此,本题就可以过了。

代码:

 

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
struct cp{double x,y;};
double pi=acos(-1);
int n,num,len;
int w[500010],l[500010],r[500010],sum[500010],sum1[500010];
ll ans;
const int m=65536;
cp cur[500010],a[500010],b[500010],c[500010];
cp operator *(cp x,cp y){return (cp){x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x};}
cp operator +(cp x,cp y){return (cp){x.x+y.x,x.y+y.y};}
cp operator -(cp x,cp y){return (cp){x.x-y.x,x.y-y.y};}
void fft(cp *a,int n,int fl)
{
    for (int i=n>>1,j=1;j<n;j++)
    {
        if (i<j) swap(a[i],a[j]);
        int k=n>>1;
        for (;k&i;i^=k,k>>=1);i^=k;
    }
    for (int m=2;m<=n;m<<=1)
    {
        cp w=(cp){cos(2*pi*fl/m),sin(2*pi*fl/m)};
        cur[0]=(cp){1,0};
        for (int i=1;i<=m;i++) cur[i]=cur[i-1]*w;
        for (int i=0;i<n;i+=m)
            for (int j=i;j<i+(m>>1);j++)
            {
                cp u=a[j],v=a[j+(m>>1)]*cur[j-i];
                a[j]=u+v;
                a[j+(m>>1)]=u-v; 
            }
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;i++) scanf("%d",&w[i]);
    len=(int)sqrt(n*log(n)/log(2));num=n/len;if (n%len) num++;
    for (int i=1;i<=num;i++){l[i]=(i-1)*len;r[i]=min(l[i]+len-1,n-1);}
    for (int i=1;i<=num;i++)
    {
        for (int j=l[i];j<=r[i];j++)
        {
            for (int k=j+1;k<=r[i];k++) if (2*w[j]-w[k]>=0) ans+=(ll)sum1[2*w[j]-w[k]];
            sum1[w[j]]++;
        }
    }
    for (int i=num;i>=1;i--)
    {
        for (int j=l[i];j<=r[i];j++)
            for (int k=j+1;k<=r[i];k++) if (2*w[k]-w[j]>=0) ans+=(ll)sum[2*w[k]-w[j]];
        for (int j=l[i];j<=r[i];j++) sum[w[j]]++;
    }
    for (int i=2;i<num;i++)
    {
        for (int j=0;j<m;j++) a[j].x=a[j].y=b[j].x=b[j].y=0;
        for (int j=0;j<l[i];j++) a[w[j]].x=a[w[j]].x+1;
        for (int j=r[i]+1;j<n;j++) b[w[j]].x=b[w[j]].x+1;
        fft(a,m,1);fft(b,m,1);
        for (int j=0;j<m;j++) c[j]=a[j]*b[j];
        fft(c,m,-1);
        for (int j=0;j<m;j++) c[j].x=(ll)(c[j].x/m+0.5);
        for (int j=l[i];j<=r[i];j++) ans+=(ll)c[2*w[j]].x;
    }
    printf("%lld\n",ans);
}

Category: FFT | Tags:
2
4
2016
0

【bzoj3527】【ZJOI2014】力

我也不知道为什么大视野上没有题面描述,所以我就只能百度了。

首先这道题有个陷阱,题面给你的是Fj,但要求的是Ei,所以我们先把qi除掉,于是Ei就变成了sigma(j<i)(q[j]/(i-j)^2)-sigma(j>i)(q[j]/(i-j)^2)。

于是,我们令a[i]=q[i],b[i]=1/(i*i);

那么,E[i]=sigma(a[j]*b[i-j])-sigma(a[j]*b[j-i]);

神奇的发现,减号左右两边都是卷积的形式,FFT直接上就行了。

代码:

 

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
double pi=acos(-1);
int n,m;
struct cp{double x,y;};
double q[500010];
cp a[500010],b[500010],cur[500010],c[500010],d[500010],A[500010],B[500010];
cp operator *(cp x,cp y){return (cp){x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x};}
cp operator +(cp x,cp y){return (cp){x.x+y.x,x.y+y.y};}
cp operator -(cp x,cp y){return (cp){x.x-y.x,x.y-y.y};}
void fft(cp *a,int n,int fl)
{
    for (int i=n>>1,j=1;j<n;j++)
    {
        if (i<j) swap(a[i],a[j]);
        int k=n>>1;
        for (;k&i;i^=k,k>>=1);i^=k;
    }
    for (int m=2;m<=n;m<<=1)
    {
        cp w=(cp){cos(2*pi*fl/m),sin(2*pi*fl/m)};
        cur[0]=(cp){1,0};
        for (int i=1;i<=m;i++) cur[i]=cur[i-1]*w;
        for (int i=0;i<n;i+=m)
            for (int j=i;j<i+(m>>1);j++)
            {
                cp u=a[j],v=a[j+(m>>1)]*cur[j-i];
                a[j]=u+v;
                a[j+(m>>1)]=u-v;
            }
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;i++)
        scanf("%lf",&q[i]);
    for (int i=0;i<n;i++)
        a[i].x=q[i];
    for (int i=1;i<n;i++)
        b[i].x=(double)1/i/i;
    m=1;while (m<=n*2) m<<=1;
    fft(a,m,1);fft(b,m,1);
    for (int i=0;i<=m;i++) c[i]=a[i]*b[i];
    fft(c,m,-1);
    for (int i=0;i<=m;i++) c[i].x=c[i].x/m;
    for (int i=0;i<n;i++) A[i].x=q[i];
    for (int i=0;i<n-1;i++) B[i].x=(double)1/(n-1-i)/(n-1-i);
    fft(A,m,1);fft(B,m,1);
    for (int i=0;i<=m;i++) d[i]=A[i]*B[i];
    fft(d,m,-1);
    for (int i=0;i<=m;i++) d[i].x=d[i].x/m;
    for (int i=0;i<n;i++) printf("%lf\n",c[i].x-d[n-1+i].x);
}

Category: FFT | Tags:
2
4
2016
0

【bzoj2179】FFT快速傅里叶

2179: FFT快速傅立叶

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 2196  Solved: 1098
[Submit][Status][Discuss]

Description

给出两个n位10进制整数x和y,你需要计算x*y。

Input

第一行一个正整数n。 第二行描述一个位数为n的正整数x。 第三行描述一个位数为n的正整数y。

Output

输出一行,即x*y的结果。

Sample Input

1
3
4
 

Sample Output

12
数据范围:
n<=60000
 

HINT

我们发现高精度乘法的每一位是满足卷积的形式的,所以我们先fft搞出c数组,然后再一位一位进位上去就可以了。

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
double pi=acos(-1);
int n,m,l,c[500010];
char s[100010];
struct cp{double x,y;};
cp a[500010],b[500010],cur[500010];
cp operator *(cp x,cp y){return (cp){x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x};}
cp operator +(cp x,cp y){return (cp){x.x+y.x,x.y+y.y};}
cp operator -(cp x,cp y){return (cp){x.x-y.x,x.y-y.y};}
void fft(cp *a,int n,int fl)
{
    for (int i=n>>1,j=1;j<n;j++)
    {
        if (i<j) swap(a[i],a[j]);
        int k=n>>1;
        for (;k&i;i^=k,k>>=1);
        i^=k;
    }
    for (int m=2;m<=n;m<<=1)
    {
        cp w=(cp){cos(2*pi*fl/m),sin(2*pi*fl/m)};
        cur[0]=(cp){1,0};
        for (int i=1;i<m;i++) cur[i]=cur[i-1]*w;
        for (int i=0;i<n;i+=m)
            for (int j=i;j<i+(m>>1);j++)
            {
                cp u=a[j],v=a[j+(m>>1)]*cur[j-i];
                a[j]=u+v;
                a[j+(m>>1)]=u-v;
            }
    }
}
int main()
{
    scanf("%d",&n);
    scanf("%s",s);
    for (int i=0;i<n;i++)
        a[i].x=s[n-i-1]-'0';
    scanf("%s",s);
    for (int i=0;i<n;i++)
        b[i].x=s[n-i-1]-'0';
    m=1;while (m<=n*2) m*=2;
    fft(a,m,1);fft(b,m,1);
    for (int i=0;i<=m;i++) a[i]=a[i]*b[i];
    fft(a,m,-1);
    for (int i=0;i<=m;i++)
        c[i]=(int)(a[i].x/m+0.5);
    for (int i=0;i<=m;i++)
    {
        c[i+1]+=c[i]/10;
        c[i]%=10;
    }
    l=m;while (!c[l]) l--;
    for (int i=l;i>=0;i--) printf("%d",c[i]);printf("\n");
}
Category: FFT | Tags:
2
4
2016
0

【bzoj2194】快速傅里叶之二

2194: 快速傅立叶之二

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 758  Solved: 433
[Submit][Status][Discuss]

Description

请计算C[k]=sigma(a[i]*b[i-k]) 其中 k < = i < n ,并且有 n < = 10 ^ 5。 a,b中的元素均为小于等于100的非负整数。

 

Input

       第一行一个整数N,接下来N行,第i+2..i+N-1行,每行两个数,依次表示a[i],b[i] (0 < = i < N)。

Output

输出N行,每行一个整数,第i行输出C[i-1]。

Sample Input

5
3 1
2 4
1 1
2 4
1 4
 

Sample Output

24
12
10
6
1

HINT

题目给的式子和卷积的形式非常像,我们只要稍作改变就行了。

由于正常的卷积是sigma(a[i]*b[k-i]),而题目里是要求sigma(a[i]*b[i-k]),那么我们可以先把b数组翻转一下,变成求sigma(a[i]*b[n-1+k-i]),发现这等于c[n-1+k],c为fft后的数组,所以对于每个i,我们只要输出c[n-1+i]就可以了。

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 500010
using namespace std;
double pi=acos(-1);
int n,m;
struct cp{double x,y;};
cp a[N],b[N],cur[N];
cp operator *(cp x,cp y){return (cp){x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x};}
cp operator +(cp x,cp y){return (cp){x.x+y.x,x.y+y.y};}
cp operator -(cp x,cp y){return (cp){x.x-y.x,x.y-y.y};}
void fft(cp *a,int n,int fl)
{
    for(int i=n>>1,j=1;j<n;j++)
    {
        if (i<j) swap(a[i],a[j]);
        int k=n>>1;
        for (;k&i;i^=k,k>>=1);i^=k;
    }
    for(int m=2;m<=n;m<<=1)
    {
        cp w=(cp){cos(2*pi*fl/m),sin(2*pi*fl/m)};
        cur[0]=(cp){1,0};
        for (int i=1;i<m;i++) cur[i]=cur[i-1]*w;
        for (int i=0;i<n;i+=m)
            for (int j=i;j<i+(m>>1);++j)
            {
                cp u=a[j],v=a[j+(m>>1)]*cur[j-i];
                a[j]=u+v;
                a[j+(m>>1)]=u-v;
            }
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;i++) scanf("%lf%lf",&a[i].x,&b[n-i-1].x);
    m=1;
    while (m<=n*2) m*=2;
    fft(a,m,1);
    fft(b,m,1);
    for (int i=0;i<m;i++) a[i]=a[i]*b[i];
    fft(a,m,-1);
    for (int i=0;i<n;i++) printf("%d\n",(int)(a[n-1+i].x/m+0.5));
}
Category: FFT | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com