2015编程之美初赛

#1156 : 彩色的树

时间限制: 2000ms
单点时限: 1000ms
内存限制: 256MB

描述

给定一棵n个节点的树,节点编号为1, 2, …, n。树中有n - 1条边,任意两个节点间恰好有一条路径。这是一棵彩色的树,每个节点恰好可以染一种颜色。初始时,所有节点的颜色都为0。现在需要实现两种操作:

1. 改变节点x的颜色为y;

2. 询问整棵树被划分成了多少棵颜色相同的子树。即每棵子树内的节点颜色都相同,而相邻子树的颜色不同。

输入

第一行一个整数T,表示数据组数,以下是T组数据。

每组数据第一行是n,表示树的节点个数。接下来n - 1行每行两个数i和j,表示节点i和j间有一条边。接下来是一个数q,表示操作数。之后q行,每行表示以下两种操作之一:

1. 若为"1",则询问划分的子树个数。

2. 若为"2 x y",则将节点x的颜色改为y。

输出

每组数据的第一行为"Case #X:",X为测试数据编号,从1开始。

接下来的每一行,对于每一个询问,输出一个整数,为划分成的子树个数。

数据范围

1 ≤ T ≤ 20

0 ≤ y ≤ 100000

小数据

1 ≤ n, q ≤ 5000

大数据

1 ≤ n, q ≤ 100000

样例输入
2
3
1 2
2 3
3
1
2 2 1
1
5
1 2
2 3
2 4
2 5
4
1
2 2 1
2 3 2
1
样例输出
Case #1:
1
3
Case #2:
1
5
思路:用map维护对于每个节点保存跟他相邻的孩子节点的每种颜色的数量,每次修改的时候,只需要修改他父节点的map,答案加上这个节点两次的颜色的数量差

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100010;
int head[maxn],tot;
int col[maxn],fa[maxn];
int N,Q;
map<int,int> mp[maxn];
struct node
{
    int v,next;
}edge[maxn*2];
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
    memset(col,0,sizeof(col));
    for(int i=0;i<=N;i++)mp[i].clear();
}
void add_edge(int u,int v)
{
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u,int pre)
{
    fa[u]=pre;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==pre)continue;
        mp[u][0]++;
        dfs(v,u);
    }
}
int main()
{
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
       scanf("%d",&N);
       init();
       for(int i=1;i<N;i++)
       {
           int u,v;
           scanf("%d%d",&u,&v);
           add_edge(u,v);
           add_edge(v,u);
       }
       dfs(1,0);
       scanf("%d",&Q);
       int ans=1;
       int op,x,y;
       printf("Case #%d:\n",cas++);
       while(Q--)
       {
           scanf("%d",&op);
           if(op==1)printf("%d\n",ans);
           else
           {
               scanf("%d%d",&x,&y);
               if(x!=1)
               {
                   int tmp=fa[x];
                   if(col[x]!=col[tmp])ans--;
                   mp[tmp][col[x]]--,mp[tmp][y]++;
                   if(y!=col[tmp])ans++;
               }
               ans+=mp[x][col[x]]-mp[x][y];
               col[x]=y;
           }
       }
    }
    return 0;
}

#1158 : 质数相关

时间限制: 2000ms
单点时限: 1000ms
内存限制: 256MB

描述

两个数a和 b (a<b)被称为质数相关,是指a × p = b,这里p是一个质数。一个集合S被称为质数相关,是指S中存在两个质数相关的数,否则称S为质数无关。如{2, 8, 17}质数无关,但{2, 8, 16}, {3, 6}质数相关。现在给定一个集合S,问S的所有质数无关子集中,最大的子集的大小。

输入

第一行为一个数T,为数据组数。之后每组数据包含两行。

第一行为N,为集合S的大小。第二行为N个整数,表示集合内的数。

输出

对于每组数据输出一行,形如"Case #X: Y"。X为数据编号,从1开始,Y为最大的子集的大小。

数据范围

1 ≤ T ≤ 20

集合S内的数两两不同且范围在1到500000之间。

小数据

1 ≤ N ≤ 15

大数据

1 ≤ N ≤ 1000

样例输入
3
5
2 4 8 16 32
5
2 3 4 6 9
3
1 2 3
样例输出
Case #1: 3
Case #2: 3
Case #3: 2

网络流:先求出每个数的质因子的个数,然后对于有偶数个质因子的数,与起点连边,奇数个的与终点连边,奇偶之间并且只差一个素数的连边,求最大流,用N减去就是答案

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int INF=1000000000;
const int limit=5*maxn;
int N;
int a[maxn];
int prime[limit],cnt[limit];
int n,m,k,nn,st,en;
int vis[maxn],pre[maxn];
int tot;
int dis[maxn],gap[maxn],cur[maxn],head[maxn];
struct node
{
    int v,f,next;
}edge[maxn*2];
void add_edge(int x,int y,int f)
{
    edge[tot].v=y;
    edge[tot].f=f;
    edge[tot].next=head[x];
    head[x]=tot++;
    edge[tot].v=x;
    edge[tot].f=0;
    edge[tot].next=head[y];
    head[y]=tot++;
}
int SAP(int st,int en)
{
    for(int i=0;i<=nn;i++)
    {
        cur[i]=head[i];
        dis[i]=gap[i]=0;
    }
    int u=0;
    int flow=0,aug=INF;
    gap[st]=nn;
    u=pre[st]=st;
    bool flag;
    while(dis[st]<nn)
    {
        flag=0;
        for(int &j=cur[u];j!=-1;j=edge[j].next)
        {
            int v=edge[j].v;
            if(edge[j].f>0&&dis[u]==dis[v]+1)
            {
                flag=1;
                if(edge[j].f<aug)aug=edge[j].f;
                pre[v]=u;
                u=v;
                if(u==en)
                {
                    flow+=aug;
                    while(u!=st)
                    {
                        u=pre[u];
                        edge[cur[u]].f-=aug;
                        edge[cur[u]^1].f+=aug;
                    }
                    aug=INF;
                }
                break;
            }
        }

        if(flag)continue;
        int mindis=nn;
        for(int j=head[u];j!=-1;j=edge[j].next)
        {
            int v=edge[j].v;
            if(dis[v]<mindis&&edge[j].f>0)
            {
                mindis=dis[v];
                cur[u]=j;
            }
        }
        if((--gap[dis[u]])==0)break;
        gap[dis[u]=mindis+1]++;
        u=pre[u];

    }
    return flow;
}
//线性求质因子个数
void process()
{
    cnt[1]=0;
    int len=0;
    for(int i=2;i<limit;i++)
    {
        if(cnt[i]==0)
        {
            prime[len++]=i;
            cnt[i]=1;
        }
        for(int j=0;j<len&&i*prime[j]<limit;j++)
        {
            cnt[i*prime[j]]=cnt[i]+1;
            if(i%prime[j]==0)break;
        }
    }
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
bool judge(int x,int y)
{
    if(x>y)swap(x,y);
    if(y%x==0&&cnt[y]==cnt[x]+1)return true;
    return false;
}
int main()
{
    int T,cas=1;
    scanf("%d",&T);
    process();
    while(T--)
    {
        scanf("%d",&N);
        for(int i=1;i<=N;i++)scanf("%d",&a[i]);
        init();
        int ans=0;
        st=0,en=N+1;
        nn=en+1;
        for(int i=1;i<=N;i++)
            if(cnt[a[i]]%2==0)add_edge(st,i,1);
            else add_edge(i,en,1);
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                if(cnt[a[i]]%2==0&&cnt[a[j]]%2==1&&judge(a[i],a[j]))
                    add_edge(i,j,1);
        printf("Case #%d: %d\n",cas++,N-SAP(st,en));
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值