1878: [SDOI2009]HH的项链

2013年3月28日 08:54

http://www.lydsy.com/JudgeOnline/problem.php?id=1878

RunID User Problem Result Memory Time Language Code_Length Submit_Time
378013 Liu_Ts 1878 Accepted 32056 kb 1260 ms C++ 1081 B 2013-03-28 08:46:15

 

题意

给定一个序列,询问每个区间上有多少不同的数字。

题解

因为没有修改,所以离线做就可以了。

把询问按右端点排序,

从前到后,对于每个颜色,

在它上一次出现的地方的后一点+1,

在当前位置的后一点-1;

当前询问答案为ΣXi(1..L)

 

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N=1000002;
int n,m;
struct rec{int n,x,y;}a[N];
int color[N],last[N],pre[N],ans[N];
int c[N];
inline bool cmp(rec a,rec b)
{
  return a.y<b.y;
}
inline void inc(int x,int w)
{
  for(;x<=n;x+=lowbit(x))c[x]+=w;
}
inline int sum(int x)
{
  int s=0;
  for(;x;x-=lowbit(x))s+=c[x];
  return s;
}
 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
	{
        scanf("%d",color+i);
        pre[i]=last[color[i]];
        last[color[i]]=i;
    }
    scanf("%d",&m);
    for(int i=0;i<m;++i)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        a[i].n=i;
    }
    sort(a,a+m,cmp);
    int now=0;
    for(int i=0;i<m;++i)
    {
        while(now<a[i].y)
            inc(pre[++now]+1,1),inc(now+1,-1);
        ans[a[i].n]=sum(a[i].x);
    }
 	for(int i=0;i<m;++i)printf("%d\n",ans[i]);
}

Tags: SDOI bzoj
评论(1) 阅读(1863)

1532: [POI2005]Kos-Dicing

2013年3月27日 17:32

http://www.lydsy.com/JudgeOnline/problem.php?id=1532

RunID User Problem Result Memory Time Language Code_Length Submit_Time
377436 Liu_Ts 1532 Accepted 3296 kb 744 ms C++ 1756 B 2013-03-27 16:58:44

 

题意

n个人,m场比赛,求赢得最多的人最少赢多少场。

题解

二分答案X;

S向每场比赛连边,容量为1;

每场比赛向参赛的两个人连边容量oo;

每个人向T连边,容量为X。

如果答案合法,最大流为m。

sap卡了,不知道为什么在这种二分图上dinic会快

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
using namespace std;
const int N=20011,M=5*N;
int u[M],v[M],w[M],next[M],head[N],cur[N],k;
int n,m,a[10001],b[10001];
int s,t,pre[N],low[N],d[N],vd[N];
queue<int> q;
void add(int x,int y,int z)
{
    u[++k]=x;v[k]=y;w[k]=z;next[k]=head[x];head[x]=k;
    u[++k]=y;v[k]=x;w[k]=0;next[k]=head[y];head[y]=k;
}
 
bool bfs()
{
    int now;bool flag=false;
    memset(d,-1,sizeof(d));
    q.push(t);d[t]=0;
    while(!q.empty())
    {
        now=q.front();q.pop();
        for(int i=head[now];i;i=next[i])
            if(d[v[i]]==-1 && w[i^1])
            {
                d[v[i]]=d[now]+1;
                q.push(v[i]);
                if(v[i]==s) flag=true;
            }
    }
    return flag;
}
int dfs(int now,int low)
{
    if(now==t)  return low;
    int ret=low,tmp;
    for(int i=head[now];i;i=next[i])
    {
        if(d[v[i]]!=d[now]-1 || !w[i])  continue;
        tmp=dfs(v[i],min(low,w[i]));
        low-=tmp;
        w[i]-=tmp;w[i^1]+=tmp;
        if(low==0)  break;
    }
    if(low==ret)d[now]=-1;
    return ret-low;
}
int solve()
{
    int ans=0,flow;
    while(bfs())
        while(flow=dfs(s,INT_MAX))
            ans+=flow;
    return ans;
}
void build(int x)
{
    memset(head,0,sizeof(head));k=1;
    for(int i=1;i<=m;i++)
    {
        add(s,i,1);
        add(i,m+a[i],1);add(i,m+b[i],1);
    }
    for(int i=1;i<=n;i++)
        add(m+i,t,x);
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",a+i,b+i);
    s=n+m+1;t=s+1;
    int l=1,r=m,mid;
    while(l<=r)
    {
        mid=l+r>>1;
        build(mid);
        if(solve()==m)  r=mid-1;
        else    l=mid+1;
    }
    printf("%d\n",l);
}

 

Tags: POI bzoj
评论(0) 阅读(1032)

1596: [Usaco2008 Jan]电话网络

2013年3月27日 15:40

http://www.lydsy.com/JudgeOnline/problem.php?id=1596

RunID User Problem Result Memory Time Language Code_Length Submit_Time
377097 Liu_Ts 1596 Accepted 3932 kb 16 ms C++ 944 B 2013-03-27 11:32:23

 

题意

一棵树,每个点可以支配直接与它相邻的点,求最小支配集。

题解

经典题目0.0

f[i][0]:以i为根的子树中均被覆盖且草地i上信号塔所需的最小塔数

f[i][1]:以i为根的子树中均被覆盖且草地i上信号塔所需的最小塔数

f[i][2]:以i为根的子树中除i点以外其余点均被覆盖所需的最小塔数

 转移方法:

f[i][1]=Σ(min(f[i.son][0..2]))

f[i][2]=Σ(f[i.son][0])

令sum=Σ(min(f[i.son][0..1]))

f[i][0]=min(f[i.son][1]+sum-min(f[i.son][0..1]))

i为叶节点时,f[i][0]显然是不合法的,所以应设为无穷大。 

 

#include<cstring>
#include<cstdio>
#include<cmath>
#include<climits>
#include<algorithm>
using namespace std;
const int N=100001,M=2*N;
int n,k,f[N][3],v[M],next[M],head[N];
 
void add(int x,int y)
{
    v[++k]=y;next[k]=head[x];head[x]=k;
}
 
void dfs(int x,int fa)
{
    int sum=0;
    f[x][1]=1;
    for(int i=head[x];i;i=next[i])
        if(v[i]!=fa)
        {
            dfs(v[i],x);
            f[x][1]+=min(f[v[i]][0],min(f[v[i]][1],f[v[i]][2]));
            f[x][2]+=f[v[i]][0];
            sum+=min(f[v[i]][0],f[v[i]][1]);
        }
    f[x][0]=N;
    if(x!=1&&!next[head[x]])    return;
    for(int i=head[x];i;i=next[i])
        if(v[i]!=fa)
            f[x][0]=min(f[x][0],f[v[i]][1]+sum-min(f[v[i]][0],f[v[i]][1]));
}
     
 
int main()
{
    scanf("%d",&n);
    int x,y;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    printf("%d\n",min(f[1][1],f[1][0]));
}

Tags: usaco bzoj
评论(0) 阅读(854)

1776: [Usaco2010 Hol]cowpol 奶牛政坛

2013年3月26日 17:21

http://www.lydsy.com/JudgeOnline/problem.php?id=1776

RunID User Problem Result Memory Time Language Code_Length Submit_Time
376495 Liu_Ts 1776 Accepted 26244 kb 1940 ms C++ 1693 B 2013-03-26 17:01:47

 

题意

一棵树,每个节点有一个编号,一共K种编号,输出每种编号的控制范围,即所有该编号的点,在树上的最远点对。

题解

对于每种编号,

最远点对中的其中一个点肯定离根最远,

所以先找出这个点,

枚举剩下的点,用lca算出每个点和找出的点的距离,更新答案即可。

 

#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<climits>
using namespace std;
const int N=200001,K=100001,M=N*2,Q=N*3;
int q[Q],d[N],num[N],p[N],Max[K],ans[N];
int v[M],next[M],head[N],k;
int n,m;

struct SegmentTree
{
	int m[Q*3];
	void update(int x)
	{
		int l=x<<1,r=l+1;
		m[x]=m[l];
		if(d[m[x]]>d[m[r]])
			m[x]=m[r];
	}
	void build(int x,int s,int t)
	{
		if(s==t)
		{
			m[x]=q[s];return;
		}
		int mid=s+t>>1;
		build(x<<1,s,mid);build((x<<1)+1,mid+1,t);
		update(x);
	}
	int get(int x,int s,int t,int ll,int rr)
	{
		if(ll<=s&&t<=rr)	return m[x];
		int t1=0,t2=0,mid=s+t>>1;
		if(ll<=mid)	t1=get(x<<1,s,mid,ll,rr);
		if(rr>mid)	t2=get((x<<1)+1,mid+1,t,ll,rr);
		if(d[t1]<d[t2])	return t1;
		else return t2;
	}
}a;
void add(int x,int y)
{
	if((!x)||(!y))	return;
	v[++k]=y;next[k]=head[x];head[x]=k;
}
void dfs(int x,int fa,int deep)
{
	q[++q[0]]=x;num[x]=q[0];d[x]=deep;
	if(d[x]>d[Max[p[x]]])	Max[p[x]]=x;
	for(int i=head[x];i;i=next[i])
		if(v[i]!=fa)
			dfs(v[i],x,deep+1),q[++q[0]]=x;
}
int lca(int x,int y)
{
	if(num[x]>num[y])	swap(x,y);
	return a.get(1,1,q[0],num[x],num[y]);
}
int main()
{
	scanf("%d%d",&n,&m);
	int x,y,z;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",p+i,&x);
		add(x,i);add(i,x);
	}
	dfs(1,0,0);d[0]=INT_MAX;
	a.build(1,1,q[0]);
	for(int i=1;i<=n;i++)
	{
		int tmp=d[i]+d[Max[p[i]]]-d[lca(i,Max[p[i]])]*2;
		ans[p[i]]=max(ans[p[i]],tmp);
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
}

Tags: usaco bzoj
评论(0) 阅读(1027)

1704: [Usaco2007 Mar]Face The Right Way

2013年3月25日 16:25

http://www.lydsy.com/JudgeOnline/problem.php?id=1704

RunID User Problem Result Memory Time Language Code_Length Submit_Time
375337 Liu_Ts 1704 Accepted 864 kb 428 ms C++ 720 B 2013-03-24 22:31:26

 

题意

给定一个01序列,每次可以将连续的k个数取反。

确定一个k使得,将所有数全变成0的操作次数m最小。

题解

枚举k,O(N)验证,更新答案。

把所有当前为1的反转。

验证时,用一个队列,记录被修改过的点,每次找到对当前枚举点有作用的队列区间,

如果区间长度为奇数,说明该点之前被反转。

 

#include<cstring>
#include<cmath>
#include<cstdio>
#include<climits>
using namespace std;
 
const int N=5001;
 
int b[N],a[N],n,m,k,q[N];
 
 
 
int check(int x)
{
    int l=0,r=1,now=0;
    memcpy(b,a,sizeof(a));
    for(int i=1;i<=n;i++)
    {
        while(l<=r && q[l]<=i-x)  l++;
        if(((r-l+1)&1)) b[i]=1-b[i];
        if(b[i])
            if(i<=n-x+1)
                now++,b[i]=0,q[++r]=i;
            else return INT_MAX;
    }
    return now;
}
 
int main()
{
    scanf("%d",&n);
    int m=INT_MAX;
    for(int i=1;i<=n;i++)
        getchar(),a[i]=getchar()=='B';
    for(int i=1;i<=n;i++)
    {
        int tmp=check(i);
        if(tmp<m)
            m=tmp,k=i;
    }
    printf("%d %d\n",k,m);
}

 

Tags: usaco bzoj
评论(0) 阅读(9248)