2015 编程之美初赛第一场 AC题
题目1 : 彩色的树
描述
给定一棵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
其实只需要考虑修改点对相邻的点影响就好。例如原来相同改成不同就+1如此。用枚举,T了大数据。正确做法应该是预先存下相邻点的情况,再处理。#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN=100050; int color[MAXN]; struct edge{ int u,v; int next; }edge[MAXN*2]; int head[MAXN]; int tot; void addedge(int u,int v){ edge[tot].u=u; edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++; } int main(){ int T,icase=0,u,v,op,x,y; scanf("%d",&T); while(T--){ int n,q; scanf("%d",&n); tot=0; memset(color,0,sizeof(int)*(n+10)); memset(head,-1,sizeof(int)*(n+10)); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } scanf("%d",&q); int ans=1; printf("Case #%d:\n",++icase); for(int i=1;i<=q;i++){ scanf("%d",&op); if(op==1) printf("%d\n",ans); else{ scanf("%d%d",&x,&y); int pre=color[x]; for(int e=head[x];e!=-1;e=edge[e].next){ int v=edge[e].v; if(color[v]==pre){ if(color[v]!=y) ans++; } else if(color[v]!=pre){ if(color[v]==y) ans--; } } color[x]=y; } } } return 0; }
题目3 : 质数相关
时间限制: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
可证此图必定无环。是一棵树,树形DP即可。同时树也是二分图,二分图最大独立集也可做。#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define LL long long using namespace std; const int MAXN=500050; bool isprime[MAXN]; int prime[MAXN]; void predo(){ memset(isprime,false,sizeof(isprime)); int tot=0; for(int i=2;i<=MAXN;i++){ if(!isprime[i]) prime[tot++]=i; for(int j=0;j<tot;j++){ if((LL)i*(LL)prime[j]>(LL)MAXN) break; isprime[i*prime[j]]=true; if(i%prime[j]==0) break; } } } int num[1010]; bool vis[1010]; struct edge{ int u,v; int next; }edge[3000]; int dp[1050][2]; int tot,head[1050]; void addedge(int u,int v){ edge[tot].u=u; edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++; } void dfs(int i){ vis[i]=true; dp[i][1]=1; dp[i][0]=0; for(int e=head[i];e!=-1;e=edge[e].next){ int v=edge[e].v; if(!vis[v]){ dfs(v); dp[i][1]+=dp[v][0]; dp[i][0]+=max(dp[v][1],dp[v][0]); } } } int main(){ predo(); int T,icase=0; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); tot=0; memset(vis,false,sizeof(vis)); memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++) scanf("%d",&num[i]); sort(num+1,num+n+1); for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(num[j]%num[i]==0){ if(!isprime[num[j]/num[i]]){ addedge(i,j); addedge(j,i); } } } } int ans=0; for(int i=1;i<=n;i++){ if(!vis[i]){ dfs(i); ans+=max(dp[i][0],dp[i][1]); } } printf("Case #%d: %d\n",++icase,ans); } return 0; }