점 분할 치료 템 플 릿 문제
문 제 를 관찰 하 는 것 은 분명 하 다. 이것 은 점 분 치 의 틀 문제 이다.그럼 분할 치료 방법 으로 해결 합 시다.그러나 점 분 치 는 어떤 방법 일 까?우선 개인 적 으로 점 분 치 는 나 무 를 계속 나 누 어 처리 하 는 방법 이다.나 무 를 나 누 려 면 나 무 를 몇 부분 으로 나 누 어 처리 해 야 한다.우 리 는 어떤 점 을 선택해 서 분할 해 야 합 니까?빠 른 배열 처럼 선택 한 '중심 점' 이 좋 지 않 으 면 빠 른 배열 의 복잡 도 는 O (n ^ 2) 에 달 할 수 있 고 점 분할 치료 에서 분 치 점 이 점 을 선택 하면 복잡 도 는 O (n) 로 변 할 수 있다.물론 빨리 배열 하 는 과정 에서 하나의 숫자 로 검색 을 해서 선택 한 숫자 가 반드시 중간 값 이 라 고 보장 하 지 는 않 지만 사람들 은 무 작위 로 숫자 를 찾 는 방법 을 선택 하여 복잡 도가 비교적 낮은 상황 에서 보장 할 수도 있다.
나무의 중심: n 개의 노드 가 없 는 나무 에 대해 점 을 찾 아 나 무 를 이 점 을 뿌리 가 있 는 나무 로 만 들 때 가장 큰 나무의 결 점 이 가장 작다.다시 말 하면 이 점 을 삭제 한 후 가장 큰 연결 블록의 결점 수가 가장 작 으 면 이 점 이 나무의 중심 이다.나무의 중심: 가장자리 에 권력 이 있 는 나 무 를 주 고 나무의 점 을 구하 여 이 점 을 나무의 다른 결점 의 가장 먼 거리 에서 가장 가 깝 게 합 니 다.
우 리 는 만약 우리 가 나무의 중심 을 얻 을 수 있다 면, 우 리 는 나무의 중심 을 분할 점 으로 하여, 잘 린 후의 모든 자 나 무 를 가능 한 한 평균 적 으로 할 수 있다 는 것 을 발견 했다. 왜냐하면 이 점 의 최대 자 나무의 결 점 이 가장 작 기 때문이다.그러면 점 분 치 의 첫 번 째 중요 한 조작 은 바로 나무 (또는 잘 린 나무) 의 중심 위 치 를 구 하 는 것 이다.
해법: 하나의 결점 을 뿌리 로 선택 하고 뿌리 없 는 나 무 를 뿌리 있 는 나무 로 만 든 다음 에 f [i] 를 설치 하여 i 를 뿌리 로 하 는 서브 나무의 결점 개 수 를 표시 합 니 다.f [i] =Σf[j] (j∈s[i]) + 1 。프로그램 구현 은 간단 합 니 다. DFS 한 번 만 있 으 면 뿌리 없 는 나무 에서 뿌리 있 는 나 무 를 돌 리 는 동시에 동기 화 계산 하면 됩 니 다.사실 결점 i 를 삭제 한 후에 가장 큰 연결 블록 은 몇 개의 결점 이 있 습 니까?결점 i 의 서브 트 리 중 가장 큰 것 은 max {f [j]} 개의 결점 이 있 고, i 의 '위쪽 서브 트 리' 에는 n - f [i] 개의 결점 이 있다.
물론, 우 리 는 f [i] 배열 을 사용 하여 노드 가 i 를 뿌리 로 하 는 서브 트 리 의 노드 개 수 를 저장 할 수 있 습 니 다.이렇게 되면 효율 이 상대 적 으로 떨 어 질 수 있 기 때문이다.또한 하나의 배열 기록 g [i] 를 추가 하여 i 를 뿌리 로 하 는 가장 큰 하위 나무의 결산 점 개 수 를 표시 하면 우 리 는 g [i] = max {f [j]} (j 는 i 의 아들) 을 얻 은 다음 에 g [i] 와 i 의 조상 과 그 나머지 부분의 결산 점 개 수 를 비교 할 수 있다 (즉 i 를 뿌리 로 하 는 하위 나 무 를 제외 한 다른 나무의 부분).i 를 뿌리 로 하 는 최대 하위 나무의 결점 개 수 를 나타 낸다.모든 g [i] 를 비교 하여 DFS (깊이 우선 검색) 과정 에서 비교 하여 현재 트 리 의 중심 을 얻 을 수 있 습 니 다.
이어서 우 리 는 분 치 의 두 번 째 중요 한 부분 을 시작 했다.점 분 치 는 용 척 된 사상 을 운용 하 였 다.우리 가 점 치 료 를 사용 할 때 중심 을 찾 은 후에 이 를 분점 으로 삼 아 먼저 분점 부터 모든 점 을 옮 겨 다 니 며 이 나무의 모든 점 거리 분점 의 크기 를 계산 한 다음 에 두 조합 을 기록 했다. 다시 말 하면 이 나 무 는 실제 적 으로 분점 을 뿌리 로 하 는 것 이다.다른 모든 결점 은 분 치 점 에 직접 연 결 된 나무 이다. 즉, 이 나 무 는 깊이 가 2 인 나무 로 간주 된다.그리고 모든 점 에서 분 치 점 까지 의 거 리 를 이용 하여 모든 점 (분 치 점 자체 포함) 간 의 거 리 를 계산 하고 배열 에 넣는다.물론 결점 간 의 거 리 를 구하 면 정렬 방법 을 활용 할 수 있다. 그러면 문제 가 설정 한 최대 범 위 를 초과 하 는 상황 을 더욱 빨리 걸 러 내 고 연산 횟수 를 줄 일 수 있다.이렇게 하면 자나무 의 결점 거리 가 길 어 지 는 경우 가 있다.예 를 들 어 한 그루 의 나무 에 결산 점 거리 분할 점 dis [i] 가 있 는데 그의 아들 의 결산 점 거리 분할 점 dis [j] 는 그들 사이 의 거 리 는 dis [j] - dis [i] 여야 한다. 그러나 우 리 는 그것 을 깊이 가 2 인 다 진 나무 로 보 는 상황 을 통 해 그들의 거 리 를 dis [j] + dis [i] 로 계산한다.그래서 우 리 는 이러한 dis [j] + dis [i] 의 상황 을 삭제 해 야 한다. 그러면 우 리 는 분할 점 을 선택 하여 이 나 무 를 몇 부분 으로 나 누 었 다.그러면 우 리 는 분 치 점 을 삭제 하고 이 나무의 중심 을 다시 구 한 다음 에 분 치 점 으로 거 리 를 계속 구 하면 dis [j] - dis [i] 의 상황 을 구 할 수 있다.물론 이런 상황 을 구 했 을 때 는 이미 최초의 분점 과 관계 가 없 었 다.그렇다면 우 리 는 어떻게 같은 나무의 결점 간 의 거 리 를 삭제 합 니까?분명 한 것 은 우리 가 분 치 점 에서 시작 하여 그의 아들 i 에서 분 치 점 까지 의 거 리 를 미리 기록 한 다음 에 분 치 점 에서 다른 결점 까지 의 거 리 를 구 하 는 것 처럼 결점 i 의 모든 아들 에서 결점 i 까지 의 거 리 를 구하 고 2 를 추가 하 는 것 이다.×dis [i], 그리고 얻 은 이 거 리 를 배열 에서 삭제 합 니 다.이렇게 하면 같은 나무의 결점 간 의 중복 상황 을 삭제 할 수 있다.분 치 점 을 삭제 한 후에 이 분 치 점 의 모든 서브 트 리 에 중심 을 두 고 계속 반복 하면 답 을 얻 을 수 있다.코드 는 다음 과 같 습 니 다:
#include
using namespace std;
const int maxn=10010;
bool vis[maxn];
int treest[maxn],h[maxn],remind[maxn],deep[maxn],dis[maxn];
int n,m,root,tot,sum;
int num[10000010];
struct edge{
int to,next,value;
}e[maxn<<1];
inline int read()
{
int x=0,f=1;
char c=getchar();
while ((c!='-')&&((c<'0')||(c>'9')))
c=getchar();
if (c=='-')
f=-1,c=getchar();
while ((c>='0')&&(c<='9'))
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
return x*f;
}
void getroot(int ,int );
void qsort(int ,int );
void divide(int );
void getdeep(int ,int );
int main()
{
n=read();
m=read();
for (int i=1,u,v,w;i0]=sum=n;
getroot(1,0);
divide(root);
for (int i=1;i<=m;i++)
{
int p;
p=read();
if (num[p])
printf("AYE
");
else
printf("NAY
");
}
return 0;
}
void getdeep(int now,int fa)
{
dis[++tot]=deep[now];
for (int jump=h[now];jump;jump=e[jump].next)
{
int t=e[jump].to;
if (t==fa||vis[t])
continue;
deep[t]=deep[now]+e[jump].value;
getdeep(t,now);
}
return ;
}
void calculate(int now,int dep,int add)
{
tot=0;
deep[now]=dep;
getdeep(now,0);
for (int i=1;i<=tot;i++)
for (int j=1;j<=tot;j++)
num[dis[i]+dis[j]]+=add;
return ;
}
void divide(int now)
{
calculate(now,0,1);
vis[now]=true;
for (int jump=h[now];jump;jump=e[jump].next)
{
int t=e[jump].to;
if (vis[t])
continue;
calculate(t,e[jump].value,-1);
root=0;
sum=remind[t];
getroot(t,0);
divide(root);
}
return ;
}
void getroot(int now,int fa)
{
remind[now]=1;
treest[now]=0;
for (int jump=h[now];jump;jump=e[jump].next)
{
int t=e[jump].to;
if (t==fa||dis[t])
continue;
getroot(t,now);
remind[now]+=remind[t];
treest[now]=max(treest[now],remind[t]);
}
treest[now]=max(treest[now],sum-remind[now]);
if (treest[now]return ;
}
void qsort(int l,int r)
{
int a=l,b=r;
int c=dis[(a+b)/2];
while (a<=b)
{
while (dis[a]while (dis[b]>c)
b--;
if (a<=b)
{
int f=dis[a];
dis[a]=dis[b];
dis[b]=f;
a++;
b--;
}
}
if (aif (lreturn ;
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.