【最小生成树】 new oj 1802
题目大意:
给定一个边带正权的连通无向图G=(V,E),其中N=|V|,M=|E|,N个点从1到N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树上?
思路:
这题在考场上已经A了,最小割解决。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#define F(i,begin,end) for(int i=begin;i<=end;i++)
using namespace std;
const int imax=1000+229;
const int dmax=4000+229;
const int bmax=4000000+229;
const int inf=1000000229;
int n,m,k,a[imax],b[imax];
int num,head[dmax],to[bmax],re[bmax],next[bmax];
int ans,S,T;
void iadd(int u,int v,int flow){
to[num]=v; re[num]=flow;
next[num]=head[u]; head[u]=num++;
}
void add(int u,int v,int flow){
iadd(u,v,flow);
iadd(v,u,0);
}
void iread()
{
scanf("%d",&n);
S=0; T=3229;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]); ans+=a[i];
add(S,1000+i,a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]); ans+=b[i];
add(1000+i,T,b[i]);
}
scanf("%d",&m);
int cl1,cl2;
int now,now2;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&k,&cl1,&cl2);
now=i;
now2=2000+i; ans+=(cl1+cl2);
add(S,now,cl1); add(now2,T,cl2);
int x;
for(int j=1;j<=k;j++)
{
scanf("%d",&x);
add(now,1000+x,inf);
add(1000+x,now2,inf);
}
}
}
int d[dmax];
queue<int> q;
bool BFS()
{
memset(d,0,sizeof(d));
q.push(S);
d[S]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int k=head[u];k!=-1;k=next[k])
{
if(re[k] && !d[to[k]])
{
d[to[k]]=d[u]+1;
q.push(to[k]);
}
}
}
return d[T]>0;
}
int DFS(int x,int c)
{
if(x==T || c==0) return c;
int r=c,f;
for(int k=head[x];k!=-1;k=next[k])
{
if(re[k] && d[to[k]]==d[x]+1)
{
f=DFS(to[k],min(re[k],r));
r-=f; re[k]-=f; re[k^1]+=f;
if(r==0) break;
}
}
if(r==c) d[x]=0; // 当前节点无法继续增广;
return c-r;
}
void iwork()
{
int kk=0;
while(BFS()) { kk+=DFS(S,inf); }
printf("%d\n",ans-kk);
}
int main()
{
iread();
iwork();
return 0;
}
【最小生成树】 new oj 1968
题目大意:
二维平面上有n个点(xi, yi),现在这些点中取若干点构成一个集合S,对它们按照x坐标排序,顺次连接,将会构成一些连续上升、下降的折线,设其数量为f(S)。如下图中,1->2,2->3,3->5,5->6(数字为下图中从左到右的点编号),将折线分为了4部分,每部分连续上升、下降。
现给定k,求满足f(S) = k的S集合个数。
(n <= 50000,0 < k <= 10)
思路:
- 首先不难想出暴力方程:
- f[i][j][0/1]表示以第i 个点结尾,折线为k,最后一条是上升或者下降。
- 转移方程:
- for(int len=1;len<=k;len++)
for(int j=0;j < i;j++) - f[i][len][0]+=f[j][len][0]+f[j][len-1][1];
f[i][len][1]+=f[j][len][1]+f[j][len-1][0];
-紧接着可以想到前面把j的循环去掉,这就可以用树状数组了。如果你把纵坐标排个序建立树状数组就可以解决,详细见代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#define F(i,begin,end) for(int i=begin;i<=end;i++)
using namespace std;
typedef long long LL;
const int mod=100007;
const int imax=50000+229;
int n,k,L;
struct Point
{
int x,y;
}p[imax];
LL ans,f[imax<<1][12][2];
LL temp[imax<<1][12][2];
bool cmp(const Point a,const Point b){ return (a.x<b.x)||(a.x==b.x && a.y<b.y);}
void iread()
{
L=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y),L=max(L,p[i].y);
sort(p+1,p+n+1,cmp);
L+=1;
}
void add(int x,int j,int k,LL v)
{
for(int i=x;i<=L;i+=(i&(-i)))
temp[i][j][k]=(temp[i][j][k]+v)%mod;
}
LL query(int x,int j,int k)
{
LL sum=0;
for(int i=x;i>0;i-=(i&(-i)))
sum=(sum+temp[i][j][k])%mod;
return sum;
}
void iwork()
{
ans=0;
for(int i=1;i<=n;i++)
{
f[i][0][0]=1; f[i][0][1]=1;
add(p[i].y,0,0,1);
add(p[i].y,0,1,1);
for(int len=1;len<=k;len++)
{
LL res1=query(p[i].y-1,len,0)+query(p[i].y-1,len-1,1);
res1=(res1+mod)%mod;
LL res2=query(L,len,1)-query(p[i].y,len,1)+query(L,len-1,0)-query(p[i].y,len-1,0);
res2=(res2+mod)%mod;
// printf("%d:%lld %lld\n",i,res1,res2);
f[i][len][0]+=res1;
f[i][len][1]+=res2;
add(p[i].y,len,0,f[i][len][0]);
add(p[i].y,len,1,f[i][len][1]);
}
}
for(int i=1;i<=n;i++)
{
// printf("%lld %lld==\n",f[i][k][0],f[i][k][1]);
ans=(ans+f[i][k][0]+f[i][k][1])%mod;
}
printf("%lld\n",ans);
}
int main()
{
iread();
iwork();
return 0;
}
【折线编号】 new oj 1870
题目大意:
现在一共有N台机器,从1到N,编号与机器一一对应。显然一共有N!种编号。然而搞出太大的动静不好,所以新的编号要求第I台机器的编号小于第I+2台机器和第I+3台机器的编号,当然如果不存在I+2,I+3,也就不用管了。
现在你的任务是求出对N台机器,有多少种满足限制的编号方案。
(0< N<=10^18)
思路: 看到了不是数论就是快速幂了。实际上看答案可以想到是一个斐波那契数列。证明如下:
显然a[i-2]
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#define F(i,begin,end) for(int i=begin;i<=end;i++)
using namespace std;
typedef long long LL;
const int p=10000001;
LL base[2][2];
LL n;
LL a[2];
void Maxtrix1()
{
LL temp[2];
temp[0]=temp[1]=0;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++) temp[i]=(temp[i]+a[j]*base[j][i])%p;
for(int i=0;i<2;i++) a[i]=temp[i];
}
void Maxtrix2()
{
LL temp[2][2];
for(int i=0;i<2;i++)
for(int j=0;j<2;j++) temp[i][j]=0;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int z=0;z<2;z++) temp[i][j]=(temp[i][j]+base[i][z]*base[z][j])%p;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++) base[i][j]=temp[i][j];
}
int main()
{
scanf("%lld",&n);
a[0]=1; a[1]=2;
base[0][0]=0; base[0][1]=1;
base[1][0]=1; base[1][1]=1;
if(n<=2) printf("%lld\n",a[n-1]);
else
{
n-=LL(2);
while(n)
{
if(n&1) Maxtrix1();
Maxtrix2();
n>>=1;
}
printf("%lld\n",a[1]);
}
return 0;
}