Tyvj P2064(点的向上维护)

P2064 - 「Poetize10」能量获取

From lydliyudong    Normal (OI)
总时限:10s    内存限制:128MB    代码长度限制:64KB

描述 Description

“封印大典启动,请出Nescafe魂珠!”随着圣主applepi一声令下,圣剑护法rainbow和魔杖护法freda将Nescafe魂珠放置于封印台上。封印台是一个树形的结构,魂珠放置的位置就是根节点(编号为0)。还有n个其它节点(编号1~n)上放置着封印石,编号为i的封印石需要从魂珠上获取Ei的能量。能量只能沿着树边从魂珠传向封印石,每条边有一个能够传递的能量上限Wi,魂珠的能量是无穷大的。作为封印开始前的准备工作,请你求出最多能满足多少颗封印石的能量需求?
注意:能量可以经过一个节点,不满足它的需求而传向下一个节点。每条边仅能传递一次能量。

输入格式 InputFormat

第一行一个整数n,表示除根节点之外其它节点的数量。
接下来n行,第i+1行有三个整数Fi、Ei、Wi,分别表示i号节点的父节点、i号节点上封印石的能量需求、连接节点i与Fi的边最多能传递多少能量。

样例输入 SampleInput [复制数据]

4
0 3 2
0 100 100
1 1 1
2 75 80

样例输出 SampleOutput [复制数据]

2

数据范围和注释 Hint

对于 100% 的数据,满足1<=n<=1000,0<=Fi<=n,0<=Ei,Wi<=100。

时间限制 TimeLimitation

各个测试点1s

此题贪心 贪最小的点,易证必最优解

Program energy;
const
   maxn=1000;
var
   n,i,j,ans:longint;
   f,e,w,num,hpos:array[0..maxn] of longint;
procedure swap(var a,b:longint);
var
   t:longint;
begin
   t:=a;a:=b;b:=t;
end;
function max(a,b:longint):longint;
begin
   if a>b then exit(a) else exit(b);
end;
function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;
procedure qsort(l,r:longint);
var
   i,j,m,p:longint;
begin
   i:=l;
   j:=r;
   m:=e[(l+r) div 2];
   repeat
      while (e[i]<m) do inc(i);
      while (e[j]>m) do dec(j);
      if (i<=j) then
      begin
         swap(e[i],e[j]);
         swap(f[i],f[j]);
         swap(w[i],w[j]);
         swap(num[i],num[j]);
         inc(i);dec(j);
      end;
   until i>j;
   if (l<j) then qsort(l,j);
   if (i<r) then qsort(i,r);

end;

procedure pour(x:longint);
var
   noww,i,j:longint;
begin
   noww:=maxlongint;
   i:=x;
   while (i<>0) do
   begin
      noww:=min(noww,w[i]);
      i:=hpos[f[i]];
   end;

   if (noww<e[x]) then exit;

   inc(ans);
   i:=x;
   while (i<>0) do
   begin
      dec(w[i],e[x]);
      i:=hpos[f[i]];
   end;
end;
begin
   readln(n);
   for i:=1 to n do
   begin
      read(f[i],e[i],w[i]);
      num[i]:=i;
   end;
   qsort(1,n);
   hpos[0]:=0;
   for i:=1 to n do hpos[num[i]]:=i;


//   for i:=1 to n do write(f[i],' ',e[i],' ',w[i]);

   ans:=0;
   for i:=1 to n do pour(i);
   writeln(ans);

end.

幸运字符串(ansistring)

幸运字符串(string)

【问题描述】

对于一个只包含0和1的字符串,如果A是幸运的,B也是幸运的,那么1AB1也是一个幸运的串。现在定义”0”是一个幸运字符串,请判断给定的字符串S是否是幸运的。

【输入格式】

第一行一个数字T,表示数据组数。

接下来T组数据,第一行字符串长度n,接下来一行一个只含01的字符串。

【输出格式】

T行,第i个串如果是幸运字符串那么输出”YES”,否则输出”NO”。

【样例输入】

3

4

1001

7

1100101

7

0110011

【样例输出】

YES

YES

NO

【数据范围】

      30%的数据满足n<=100;

50%的数据满足n<=300;

100%的数据满足n<=800,T<=10

 

未1A的原因,又没改ansistring

//string
var
   n,tt,i,j,now:longint;
   s:ansistring;
function sort(i,j:longint):boolean;
var
   k:longint;
begin







   if (i=j) then
   begin
      if s[i]='0' then exit(true)
      else exit(false);
   end;
 {
   if (s[i]='1') and (s[j]='1') then
   begin
      for k:=i+1 to j-2 do
         if (sort(i+1,k)) and (sort(k+1,j-1)) then exit(true);

   end;
 }
   exit(false);


end;
begin
   assign(input,'string.in');
   assign(output,'string.out');
   reset(input);
   rewrite(output);
   readln(tt);
   while (tt>0) do
   begin
      readln(n);
      readln(s);
      while (true) do
      begin
         i:=pos('1001',s);
         if (i=0) then break;
         delete(s,i,2);
         delete(s,i+1,1);
         dec(n,3);
      end;





      if (sort(1,n)) then writeln('YES')
      else writeln('NO');

      dec(tt);
   end;
   close(input);
   close(output);
end.

传纸条(看清题目)

传纸条(message)

【题目描述】

小N和小A上课喜欢传纸条。

传纸条是有风险的,为了在老师发现的时候不知道他们在讨论什么内容,他们发明了一系列的加密方式。

其中有一种是这样的:一个数字由两个字符串a和b表达,这个数字就是b在a中匹配的位置。比如,a=”abcd”,b=”c”,那么这个数字就是3。

但是这样会出现一个问题,a和b能够表达两个不同的数字:比如,a=“ababa”,b=”aba”时,那个数字可以是1也可以是3。

他们对这种现象很好奇,现在给定一个字符串a,求一个整数x使得对于任意一个长度大于x的串b,这一问题都不会出现。

【输入数据】

一个仅由小写字母组成的字符串a

【输出数据】

一行一个整数,表示x的最小值

【样例输入】

ababa

【样例输出】

3

【数据范围】

对于50%的数据,a的长度≤10,

对于100%的数据,a的长度≤100.


Program message;
var
   n,i,j,k,l,ans:longint;
   s:string;
function min(a,b:longint):longint;
begin
   if (a<b) then exit(a) else exit(b);
end;
function max(a,b:longint):longint;
begin
   if (a>b) then exit(a) else exit(b);
end;

begin
   assign(input,'message.in');
   assign(output,'message.out');
   reset(input);
   rewrite(output);


   readln(s);
   n:=length(s);
   ans:=0;
   for i:=1 to n do
      for j:=i+1 to n do
      begin
         k:=i;
         l:=j;
         while ((s[k]=s[l]) and (l<=n)) do
         begin
            inc(k);
            inc(l);
            if (l>n) then break;
         end;


         ans:=max(ans,k-i);

      end;
   writeln(ans);

   close(input);
   close(output);

end.

 

HDU 1865(高精斐波那契)

高精斐波那契

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<string>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (200+10)
#define F (1000)
struct bign
{
	int len;
	int a[1000];
	bign operator+(const bign& b)
	{
		bign c;
		memset(c.a,0,sizeof(c.a));
		c.len=max(len,b.len)+1;
		for (int i=1;i<=c.len;i++)
		{
			c.a[i]+=a[i]+b.a[i];
			c.a[i+1]+=c.a[i]/F;
			c.a[i]=c.a[i]%F;
		}
		if (c.a[c.len]==0&&c.len>1) c.len--;
		return c;
	}
	bign operator= (int num)
	{
		memset(a,0,sizeof(a));
		a[1]=num;
		len=1;
		return *this;
	}


};







bign f[MAXN];
int n;
int main()
{
	cin>>n;
	f[1]=1;f[2]=2;


	for (int i=3;i<=200;i++)
		f[i]=f[i-1]+f[i-2];

	for (int i=1;i<=n;i++)
	{
		string s;
		cin>>s;
		int j=s.length();
		cout<<f[j].a[f[j].len];
		for (int k=f[j].len-1;k>=1;k--)
		{
			if (f[j].a[k]<100) cout<<"0";
			if (f[j].a[k]<10) cout<<"0";
			printf("%d",f[j].a[k]);
		}
		cout<<"n";



	}
//	for (int i=1;i<=200;i++) cout<<f[i].len;

	/*
	for (int j=1;j<=200;j++)
	{
		cout<<f[j].a[f[j].len];
		for (int k=f[j].len-1;k>=1;k--)
		{
			if (f[j].a[k]<100) cout<<"0";
			if (f[j].a[k]<10) cout<<"0";
			printf("%d",f[j].a[k]);
		}
		cout<<"n";

	}*/
	return 0;
}

行车(a1*b1+a1*b2+..a1*bn+a2*b1+…an*bn=(a1+..an)(b1+..bn) )

行车
(bicycle.pas/cpp)
题目描述
骑在自行车上,让微风追逐着他衣角,在不经意间捕获着一颗颗芳心,骄阳似乎也没有此时的他耀眼,这便是机房的骄傲——建德!
这是每天都会发生在附中门口的一幕。而为了每天能够领略不同的风景,捕获更多的芳心,建德打算制定n 条线路。为了简化起见,我们把这个世界想象成一个平面直角坐标系,而建德所在的福建师大附中则为原点。由于建德不能绕的太进,他每次路线的目的地都被限制在一个对应的右上角为(x, y),左下角为(-x,-y)的矩形内。
每次建德都会从原点直接沿直线走到目的地。显然,他走过了一个向量,这被数学控的“毛毡”称为这次的路线向量。他为了更好地规划线路,为每条线路定义了一个无聊值,即这次的路线向量和其余所有乊前的线路的向量的点积和【对于向量(x1,y1),(x2,y2),他们的点积和即为x1*x2+y1*y2】。建德希望合理的选择目的地,使得所有线路的无聊值乊和最小。
输入格式
第一行一个正整数n ,表示建德打算制定n 条旅行线路。
接下来 n 行,每行两个整数x , y ,描述一个限制目的地的矩形。
输出格式
一行一个整数,即最小的无聊值,保留 2 位小数。
样例输入
2
1 2
2 1
样例输出
-4.00
数据范围与约定
对于10% 的数据,保证0<n<=5,0<x,y<=5。
对于 30% 的数据,保证0<n<=20,0<x,y<=100。

对于 100% 的数据,保证0<n<=200,0<x,y<=200。

首先 根据公式  n个数和m个数两两相乘的结果为n个数的和与m个数的和的积

而且x和y互不影响

于是套公式-两两相乘/2-交集   结果为 f(n)=  (x1+..+xn)^2+x1^2+..xn^2  )/2

易证  f(n)=(x1+...+xn-1)* xn   + f(n-1)

所以  xn 要么最大,要么最小 才能使 f(n)有最大/最小值

因为不知道 (x1+..x(n-1)的正负 所以只能靠猜( 既考虑最大,也考虑最小)

回到原公式 显然  要是 f(n)有最小值 就要另 (x1+..+xn)有最小值

于是就是分组背包各种做……

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (200+10)
#define MAXV ((40000+100)*2)
#define MAXX (200+10)
#define f(i,j)  f[ (i) ][ (j)+20000   ]
int n;
long long ans=0;
bool f[MAXN][MAXV];
int x[MAXN];
int y[MAXN];
int sqr(int x)
{
	return x*x;
}
int main()
{
	freopen("bicycle.in","r",stdin);
	freopen("bicycle.out","w",stdout);
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		cin>>x[i]>>y[i];
		ans-=(long long)(sqr(x[i]))+sqr(y[i]);
	}
	memset(f,0,sizeof(f));
	f(0,0)=1;

	for (int i=1;i<=n;i++)
		for (int j=-i*200;j<=i*200;j++)
			f(i,j)=f(i-1,j-x[i])||f(i-1,j+x[i]);

	int j=0;

	while (!f(n,j)&&!f(n,-j)) j++;
	ans+=(long long)j*j;

//	cout<<j<<endl;

/*	int j=0;
	for (int i=0;i<=n;i++)
	{
		for (int j=-10;j<=10;j++)
			cout<<f(i,j);
		cout<<endl;
	}
*/
	memset(f,0,sizeof(f));
	f(0,0)=1;

	for (int i=1;i<=n;i++)
		for (int j=-i*200;j<=i*200;j++)
			f(i,j)=f(i-1,j-y[i])||f(i-1,j+y[i]);

	j=0;
	while (!f(n,j)&&!f(n,-j)) j++;

	ans+=(long long)j*j;

	cout.setf(ios::fixed);
	cout.precision(2);
	cout<<double(ans)/2<<endl;


//	while (1);



	return 0;
}

Tyvj Q1024(double的使用)

在C++中 double会以牺牲最小数为代价换取高位

另外请看好题目是求整数部分还是四舍五入

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
#include<string>
using namespace std;
#define MAXN (1000+10)
int a[MAXN][MAXN],n;
int main()
{
	long double ans=0.0;
	scanf("%d",&n);
	long long all=0;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
			all+=((long long)n-i+1)*((long long)n-j+1);
	}

	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
	{
		scanf("%d",&a[i][j]);
		ans+=(long double)(a[i][j])*(long double)(n-i+1)*(long double)(n-j+1)*(long double)(i*j);

	//	cout<<ans<<' ';
	}

	cout.setf(ios::fixed);
	cout.precision(0);
	cout<<trunc(ans/(long double)(all))<<endl;



	return 0;
}

Tyvj P2060(别把字符搞萎)

这题送分题居然wa了

我仔仔细细地看了看题目……发现不慎把Yes 和No打萎了……

仅以为戒

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
#include<string>
using namespace std;
#define MAXN (10000+10)
int w[MAXN];
int a[MAXN];
string s;
long long n,t;



int main()
{
//	freopen("read.in","r",stdin);
//	freopen("read.out","w",stdout);
	cin>>t>>n;
	for (int i=(int)('a');i<=(int)('z');i++) {
		cin>>w[i];
	}

//	cout<<w[112];
	for (int i=1;i<=n;i++)
	{
		cin>>s;
		a[i]=0;
		for (int j=0;j<s.length();j++)
		{
			s[j]=tolower(s[j]);
		//	cout<<w[(tolower(s[i]))]<<' ';
			a[i] =a[i]+ w[s[j]];
		//	cout<<a[i]<<' '<<endl;
		}
	}
	sort(a+1,a+1+n);
//	for (int i=1;i<=n;i++) cout<<a[i]<<' ';

	for (int i=1;i<=n;i++)
	{
		t-=(long long)(a[i]);
		if (t<(long long)0)
		{
			cout<<"Non";
			cout<<(i-1)<<endl;
			return 0;

		}
	}
	cout<<"Yesn"<<t<<endl;

	return 0;
}

数字 (求2类数∩的方法)

数字

(num.c/cpp/pas)

【问题描述】

    一个数字被称为好数字当他满足下列条件:

    1. 它有2*n个数位,n是正整数(允许有前导0)

    2. 构成它的每个数字都在给定的数字集合S中。

    3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等

    例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。

    已知n,求合法的好数字的个数mod 999983。

【输入格式】

    第一行一个数n。

    接下来一个长度不超过10的字符串,表示给定的数字集合。

【输出格式】

    一行一个数字表示合法的好数字的个数mod 999983。

【样例输入】

    2

    0987654321

【样例输出】

    1240

【数据规模】

    对于20%的数据,n≤7。

    对于100%的.据,n≤1000,|S|≤10。

这题难点在求前n位之和与后n位之和相等与它奇数位之和与偶数位之和相等的交集

……

记得第一个数一定要(long long ) c++中对 3000这样的常量都是算int的,而等号优先级最低

另外本题是乘法原理+加法原理

先用乘法原理 求出 A=x a=y B=y b=x 的方案数 记得%F

然后将所有的情况相加++

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
using namespace std;
#define F (999983)
#define MAXN (1000+10)
#define MAXM (10000+10)
int n,m;
char s[100];
int a[100],siz=0;
int f[MAXN][MAXM];
int main()
{
    freopen("num.in","r",stdin);
  freopen("num.out","w",stdout);
    scanf("%d",&n);
    scanf("%s",s);
    memset(f,0,sizeof(f));
    for (int i=0;i<strlen(s);i++)
        a[++siz]=s[i]-'0';

//    for (int i=1;i<=siz;i++) cout<<a[i]<<' ';
    sort(a+1,a+1+siz);
      for (int i=1;i<=siz;i++)
      {
          f[1][a[i]]=1;
//          cout<<a[i]<<' ';
      }
    f[0][0]=1;

    for (int i=2;i<=n;i++)
        for (int j=i*a[1];j<=i*a[siz];j++)
        {
   /*         if (j==18)
            {
                      cout<<'s';
                      }
   */         for (int k=1;k<=siz;k++)
                if (j-a[k]>=0)
                     f[i][j]=(f[i][j]+(f[i-1][j-a[k]])%F)%F;
        }
    long long ans=0;
    for (int j=0;j<=n*a[siz];j++)
    {
 //   if (f[n][j]>0) cout<<j<<' '<<f[n][j]<<endl;
        ans=ans+(long long)(f[n][j])*(long long)(f[n][j]);
    	ans%=F;
	}


  //  cout<<(2*ans)%F<<endl;

  	/*现在开始几算相同的情况
  	A 前奇 B 前偶 a 后奇 b 后偶

	  显然 A+a=B+b A+a=B+b  ->A=b&&sa=B

	  则答案为

	  ∑(a*A*b*B)    这4个数为(此处a,A,b,B指当它们正好等于这个值的方案数
	  ∑ (a^2*A^2)
	  ∑ (f[n/2.i]^2*f[n/2+n%2,i])

	*/
//	cout<<ans<<endl;
	long long tmpA=0;
	for (int i=(n/2)*a[1];i<=(n/2)*a[siz];i++)
	  tmpA=(tmpA+(long long)f[n/2][i]*f[n/2][i])%F;

	long long tmpB=0;
	for (int i=(n/2+n%2)*a[1];i<=(n/2+n%2)*a[siz];i++)
	  tmpB=(tmpB+(long long)f[n/2+n%2][i]*f[n/2+n%2][i])%F;

//	cout<<tmpA<<' '<<tmpB;

//	 cout<<tmpA*tmpB<<endl;

	ans=(ans*2)%F;
//	cout<<ans<<endl;

    {
//		cout<<ans<<' '<<tmpA*tmpB;
	}




	ans=(ans-tmpA*tmpB+F*((tmpA*tmpB)/F)+F)%F;


    cout<<ans<<endl;



 //  while(1);
   return 0;
}

 

比赛 (long double 与fixed)

比赛

 (mat.pas/c/cpp)

【问题描述】

    有两个队伍A和B,每个队伍都有n个人。这两支队伍之间进行n场1对1比赛,每一场都是由A中的一个选手与B中的一个选手对抗。同一个人不会参加多场比赛,每个人的对手都是随机而等概率的。例如A队有A1和A2两个人,B队有B1和B2两个人,那么(A1 vs B1,A2 vs B2)和(A1 vs B2,A2 vsB1)的概率都是均等的50%。

    每个选手都有一个非负的实力值。如果实力值为X和Y的选手对抗,那么实力值较强的选手所在的队伍将会获得(X-Y)^2的得分。

    求A的得分减B的得分的期望值。

【输入格式】

    第一行一个数n表示两队的人数为n。

    第二行n个数,第i个数A[i]表示队伍A的第i个人的实力值。

    第三行n个数,第i个数B[i]表示队伍B的第i个人的实力值。

【输出格式】

    输出仅包含一个实数表示A期望赢B多少分。答案保留到小数点后一位(注意精度)

【样例输入】

    2

    37

    15

【样例输出】

    20.0

【数据规模】

    对于30%的数据,n≤50。

    对于100%的.据,n≤50000;A[i],B[i]≤50000。

 

∑(a[i]-b[i))^2

注意后缀数要在排序后T_Y

另外C++要用long double ---但是不能用.lf占位符

要用如下的格式

cout.setf(ios::fixed);
	cout.precision(1);



	cout<<ans/n<<endl;

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
using namespace std;
#define MAXN (50000+10)
long long n,a[MAXN],b[MAXN],sumb[MAXN],sumb2[MAXN];
int main()
{
    freopen("mat.in","r",stdin);
    freopen("mat.out","w",stdout);

    scanf("%d",&n);
    sumb[0]=sumb2[0]=0;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
	}
    sort(a+1,a+1+n);
	for (int i=1;i<=n;i++)
    {
        cin>>b[i];
}
    sort(b+1,b+1+n);

	for (int i=1;i<=n;i++)
	{
		sumb[i]=sumb[i-1]+b[i];
        sumb2[i]=sumb2[i-1]+b[i]*b[i];
	}


	long double ans=0.0;


	for (int i=1;i<=n;i++) cout<<sumb[i]<<' ';
	cout<<endl;
	int r=0;
    for (int i=1;i<=n;i++)
    {
        while (r<n&&a[i]>b[r+1]) r++;


        ans+=a[i]*a[i]*(2*r-n);
        ans+=2*sumb2[r]-sumb2[n];

        ans-=2*a[i]*(2*sumb[r]-sumb[n]);

        /*
		long double tmp=0.0;
        //cout<<r<<endl;

		tmp+=r*a[i]*a[i]-2*a[i]*sumb[r]+sumb2[r];
        cout<<tmp<<endl;

		tmp-=(n-r)*a[i]*a[i]-2*a[i]*(sumb[n]-sumb[r])+sumb2[n]-sumb2[r];
        tmp/=n;

        ans+=tmp;


//		cout<<ans;
	*/
    }

//	cout<<ans<<endl;

	cout.setf(ios::fixed);
	cout.precision(1);



	cout<<ans/n<<endl;



//  while (1);
    return 0;
}

祖孙询问 (模拟栈代替DFS)

祖孙询问

(tree.pas/c/cpp)

【问题描述】

    已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。

【输入格式】

    输入第一行包括一个整数n表示节点个数。

    接下来n行每行一对整数对a和b表示a和b之间有连边。如果b是-1,那么a就是树的根。

    第n+2行是一个整数m表示询问个数。

    接下来m行,每行两个正整数x和y。

【输出格式】

    对于每一个询问,输出1:如果x是y的祖先,输出2:如果y是x的祖先,否则输出0。

【样例输入】

10

234 -1

12 234

13 234

14 234

15 234

16 234

17 234

18 234

19 234

233 19

5

234233

23312

23313

23315

23319

【样例输出】

1

0

0

0

2

【数据规模】

    对于30%的数据,n,m≤1000。

    对于100%的.据,n,m≤40000,每个节点的编号都不超过40000。

先看看标准方法求LCA(msm法):

为了处理爆栈而进行各种压缩变量,卡内存过关(边表记得*2)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
using namespace std;
#define MAXN (80000+10)
/*
#define f( i , j ) f[ ( i ) + 2 , j ]
#define b( i ) b [ ( i ) + 2  ]
*/
int n,m,f[MAXN][100],len[MAXN],deep[MAXN];
int head[MAXN],next[MAXN],edge[MAXN],siz=0;
int log2(int x)
{
    return (int)trunc(log(x)/log(2));
}
void addedge(int u,int v)
{
     edge[++siz]=v;
     next[siz]=head[u];
     head[u]=siz;
}
int a[MAXN]={0},tot=0;
bool b[MAXN];
void dfs(int x,int dep)
{
     /*
     cout<<x<<':';
    for (int i=1;i<=tot;i++) cout<<a[i]<<' ';
    cout<<endl;
    */
    b[x]=1;
    int p=head[x];
    while (p)
    {
         int now=edge[p];
         tot++;
         a[tot]=now;
         if (!b[now]) dfs(now,dep+1);
         tot--;
         p=next[p];
    }
    /*
     cout<<x<<':';
    for (int i=1;i<=tot;i++) cout<<a[i]<<' ';
    cout<<endl;
    */
    int j=0;
    for (int i=tot-1;i>=1;i-=(tot-i))
    {
        f[x][j]=a[i];
        j++;
    }
    len[x]=j-1;
    deep[x]=dep;
}
int father(int x,int y)
{
    while (y)
    {
          int p=log2(y);
          x=f[x][p];
          y-=(1<<p);
    }
    return x;
}
int main()
{
    freopen("tree.in","r",stdin);
  freopen("tree.out","w",stdout);

    memset(head,0,sizeof(head));
    memset(next,0,sizeof(next));
    memset(edge,0,sizeof(edge));
    memset(len,0,sizeof(len));
    memset(b,0,sizeof(b));
    memset(deep,0,sizeof(deep));
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x+2,y+2);addedge(y+2,x+2);
    }
    a[1]=1;tot=1;
    dfs(1,0);
    /*
    for (int i=1;i<=n+2;i++)
    {
        cout<<i<<' '<<len[i]<<':';
        for (int j=0;j<=len[i];j++) cout<<f[i][j]<<' ';
        cout<<endl;

    }
    */




    scanf("%d",&m);



    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x+=2;y+=2;
      //  cout<<x<<' '<<y<<' '<<deep[x]<<' '<<deep[y]<<endl;
        if (x==1) printf("1n");
        else if (y==1) printf("2n");
        else
        {
            int depx=deep[x],depy=deep[y];
            if (depx<depy)
            {
               if (father(y,depy-depx)==x) printf("1n");
               else printf("0n");
            }
            else if (depx>depy)
            {
                 if (father(x,depx-depy)==y) printf("2n");
                 else printf("0n");
            }
            else printf("0n");
        }

    }

  //  while (1);


    return 0;
}

考虑欧拉路径——它可以判断2个节点的从属情况

模拟栈版本:巧用while循环代替dfs

Program tree;
const
   maxn=40000;
   maxm=40000;
var
   n,m,i,j,u,v,size,x,y:longint;
   head,next,edge:array[-1..maxm*2] of longint;
// dfs
   stack:array[1..maxm*10] of longint;
   b:array[-1..maxn] of boolean;
   l,r,father:array[-1..maxn] of longint;
   time,nowstack:longint;
Procedure addedge(u,v:longint);
begin
   inc(size);
   edge[size]:=v;
   next[size]:=head[u];
   head[u]:=size;
end;
Procedure dfs;
var
   i,j,p,now,v:longint;
   flag:boolean;
begin
   time:=0;nowstack:=1;
   stack[1]:=-1;
   while (nowstack>0) do
   begin
      flag:=false;
      now:=stack[nowstack];
      inc(time);

      if not(b[now]) then begin b[now]:=true;l[now]:=time; end;



      p:=head[now];
      while (p>0) do
      begin
         if not(b[edge[p]]) then
         begin
            v:=edge[p];
            father[v]:=now;
            inc(nowstack);
            stack[nowstack]:=v;
            flag:=true;break;
         end;
         p:=next[p];
      end;
      if flag then continue;
      inc(time);  r[now]:=time; dec(nowstack);
   end;
end;
begin
   assign(input,'tree.in');
   assign(output,'tree.out');
   reset(input);
   rewrite(output);
   read(n);
   fillchar(head,sizeof(head),0);
   fillchar(l,sizeof(l),0);
   fillchar(r,sizeof(r),0);
   fillchar(b,sizeof(b),false);
   size:=0;
   for i:=1 to n do
   begin
      read(u,v);
      addedge(u,v); addedge(v,u);
   end;

   dfs;

   read(m);
   for i:=1 to m do
   begin
      read(x,y);
      if (l[x]<l[y]) and (r[y]<r[x]) then writeln('1')
      else if (l[y]<l[x]) and (r[x]<r[y]) then writeln('2')
      else writeln('0');
   end;

   close(input);
   close(output);
end.