hdu5329(2015多校4)--Question for the Leader
2015-08-01 17:16
471 查看
题目链接:点击打开链接
题目大意:给出一个图,有n条点,n条边,如果要将这个图分为相同点数的k份,要求每一份内部都可以相互链接,那么一共会有几个k符合条件。
1、有一个定理,如果一颗树要分成k个节点一份的子树,那么有n/k个节点所对应子树的节点数是k的倍数。
根据上面这个定理,那么我们就可以计算一颗树能不能被分成n/k份,题目中给出了是一棵树+一条边,所以最终应该是一个环和以环上节点为根的子树,如果环上消除一条边,那么就变成了一颗树,就可以使用定理来判断了。
首先找出环上的节点,和以节点为根的子树大小。子树中节点数是不会变得可以直接使用。但是环中的节点数是会随着删除的边而变化的,所以要枚举删除的边,找到节点数是k的倍数的节点,如果是n/k个,那么证明k是可以的。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std ;
#pragma comment(linker,"/STACK:102400000,102400000") ;
#define maxn 100000+10
struct node{
int v , next ;
}edge[maxn<<1] ;
int head[maxn] , h_cnt ;
int vis[maxn] ;
int a[maxn] , c[maxn] , n ;
int s[maxn] , m ;
int dp[maxn] ;
int cnt[maxn] , sum[maxn] ;
void add(int u,int v) {
edge[h_cnt].v = v ;
edge[h_cnt].next = head[u] ; head[u] = h_cnt++ ;
}
int f(int x) {
return c[x] == x ? x : c[x] = f(c[x]) ;
}
int find1(int x,int y) {
if( f(x) == f(y) ) return 1 ;
c[ f(x) ] = f(y) ;
return 0 ;
}
void dfs(int u) {
dp[u] = 1 ;
int i, v ;
for(i = head[u] ; i != -1 ; i = edge[i].next) {
v = edge[i].v ;
if( vis[v] ) continue ;
dfs(v) ;
dp[u] += dp[v] ;
}
return ;
}
int solve(int k) {
int i , max1 , temp = 0 ;
memset(cnt,0,sizeof(cnt)) ;
for(i = 1 ; i <= n ; i++) {
if( vis[i] ) continue ;
if( dp[i]%k == 0 ) temp++ ;
}
sum[0] = 0 ;
for(i = 1 ; i<= m ; i++) {
sum[i] = (sum[i-1] + dp[ s[i] ])%k ;
cnt[ sum[i] ]++ ;
}
max1 = cnt[ sum[m] ] + temp ;
for(i = 1 ; i < m ; i++) {
cnt[ sum[i] ]-- ;
cnt[ (sum[m]+sum[i])%k ]++ ;
max1 = max(max1,cnt[ (sum[m]+sum[i])%k ]+temp) ;
}
if( max1 == n/k ) return 1 ;
return 0 ;
}
int main() {
int i , j , x , k ,ans ;
//freopen("1003.in","r",stdin) ;
//freopen("1111.out","w",stdout) ;
while( scanf("%d", &n) != EOF ) {
memset(head,-1,sizeof(head)) ;
h_cnt = 0 ;
memset(vis,0,sizeof(vis)) ;
for(i = 1 ; i <= n ; i++) {
scanf("%d", &a[i]) ;
add(a[i],i) ;
c[i] = i ;
}
for(i = 1 ; i <= n ; i++) {
if( find1(i,a[i]) )
break ;
}
m = 0 ;
s[ ++m ] = i ;
vis[i] = 1 ;
x = a[i] ;
while( x != s[1] ) {
s[++m] = x ;
vis[x] = 1 ;
x = a[x] ;
}
for(i = 1 ; i <= m ; i++)
dfs(s[i]) ;
ans = 0 ;
for(k = 1 ; k <= n ; k++) {
if( n%k ) continue ;
ans += solve(k) ;
}
printf("%d\n", ans) ;
}
return 0 ;
}
/*
12
7 9 12 2 3 4 8 6 10 5 8 1
*/
题目大意:给出一个图,有n条点,n条边,如果要将这个图分为相同点数的k份,要求每一份内部都可以相互链接,那么一共会有几个k符合条件。
1、有一个定理,如果一颗树要分成k个节点一份的子树,那么有n/k个节点所对应子树的节点数是k的倍数。
根据上面这个定理,那么我们就可以计算一颗树能不能被分成n/k份,题目中给出了是一棵树+一条边,所以最终应该是一个环和以环上节点为根的子树,如果环上消除一条边,那么就变成了一颗树,就可以使用定理来判断了。
首先找出环上的节点,和以节点为根的子树大小。子树中节点数是不会变得可以直接使用。但是环中的节点数是会随着删除的边而变化的,所以要枚举删除的边,找到节点数是k的倍数的节点,如果是n/k个,那么证明k是可以的。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std ;
#pragma comment(linker,"/STACK:102400000,102400000") ;
#define maxn 100000+10
struct node{
int v , next ;
}edge[maxn<<1] ;
int head[maxn] , h_cnt ;
int vis[maxn] ;
int a[maxn] , c[maxn] , n ;
int s[maxn] , m ;
int dp[maxn] ;
int cnt[maxn] , sum[maxn] ;
void add(int u,int v) {
edge[h_cnt].v = v ;
edge[h_cnt].next = head[u] ; head[u] = h_cnt++ ;
}
int f(int x) {
return c[x] == x ? x : c[x] = f(c[x]) ;
}
int find1(int x,int y) {
if( f(x) == f(y) ) return 1 ;
c[ f(x) ] = f(y) ;
return 0 ;
}
void dfs(int u) {
dp[u] = 1 ;
int i, v ;
for(i = head[u] ; i != -1 ; i = edge[i].next) {
v = edge[i].v ;
if( vis[v] ) continue ;
dfs(v) ;
dp[u] += dp[v] ;
}
return ;
}
int solve(int k) {
int i , max1 , temp = 0 ;
memset(cnt,0,sizeof(cnt)) ;
for(i = 1 ; i <= n ; i++) {
if( vis[i] ) continue ;
if( dp[i]%k == 0 ) temp++ ;
}
sum[0] = 0 ;
for(i = 1 ; i<= m ; i++) {
sum[i] = (sum[i-1] + dp[ s[i] ])%k ;
cnt[ sum[i] ]++ ;
}
max1 = cnt[ sum[m] ] + temp ;
for(i = 1 ; i < m ; i++) {
cnt[ sum[i] ]-- ;
cnt[ (sum[m]+sum[i])%k ]++ ;
max1 = max(max1,cnt[ (sum[m]+sum[i])%k ]+temp) ;
}
if( max1 == n/k ) return 1 ;
return 0 ;
}
int main() {
int i , j , x , k ,ans ;
//freopen("1003.in","r",stdin) ;
//freopen("1111.out","w",stdout) ;
while( scanf("%d", &n) != EOF ) {
memset(head,-1,sizeof(head)) ;
h_cnt = 0 ;
memset(vis,0,sizeof(vis)) ;
for(i = 1 ; i <= n ; i++) {
scanf("%d", &a[i]) ;
add(a[i],i) ;
c[i] = i ;
}
for(i = 1 ; i <= n ; i++) {
if( find1(i,a[i]) )
break ;
}
m = 0 ;
s[ ++m ] = i ;
vis[i] = 1 ;
x = a[i] ;
while( x != s[1] ) {
s[++m] = x ;
vis[x] = 1 ;
x = a[x] ;
}
for(i = 1 ; i <= m ; i++)
dfs(s[i]) ;
ans = 0 ;
for(k = 1 ; k <= n ; k++) {
if( n%k ) continue ;
ans += solve(k) ;
}
printf("%d\n", ans) ;
}
return 0 ;
}
/*
12
7 9 12 2 3 4 8 6 10 5 8 1
*/
相关文章推荐
- dojo中的build(或叫压缩)
- STM32学习(1) 将外设封装成Arduino风格的类库
- 隐藏UITableview自定义cell中UITexField的弹出键盘
- SDUT2015暑假集训14级周赛1 C - 曼联(积分+求最大公yue数)
- VK Cup 2015 - Finals F. Clique in the Divisibility Graph
- UI02_UITextField及各类继承关系
- 使用WindowsBuilder进行可视化设计时不能预览界面
- Selenium2学习-024-WebUI自动化实战实例-022-网站不同分辨率下页面样式展示兼容性问题解决方案 -- 设置浏览器显示区域大小(无人值守,节约测试成本的福音,BOSS 最爱)
- UIView UILabel UITextFiled UIButton UIControlView UIAlertView
- c# ConcurrentQueue
- <property name="typeAliasesPackage" value="cn.itcast.core.bean"/>的作用
- Uilabel 字体
- hdu 4027 Can you answer these queries?
- windows 8.1 专业版 visual stuido 2015 安装失败
- UE和UI的概念
- Selenium2学习-023-WebUI自动化实战实例-021-获取浏览器显示区域大小,通过 WebDriver 截图功能
- Unique Binary Search Tree II
- Postgresql: UUID的使用
- Subsequence
- UIButton常用属性小结(编辑中。。。)