清北学堂 2017-10-07

********今天得了在清北的最高分,有点开心wwwww,mjy爱您!

 

树 
【问题背景】 
zhx 是二叉查找树大师。 
【问题描述】 
二叉查找树是一种特殊的二叉树(每个节点最多只有两个儿子的树)。树的
每个节点上存有一个唯一的值,并且满足:这个节点的左子树内所有点的值都
比这个节点的值小,且右子树内所有点的值都比这个节点的值要大。 
对于一棵二叉查找树T,我们可以将一个值为x的新点插入T中,且保持
树的性质。算法如下:


需要将x插入树T时,执行insert(x, T.root) 。 
现在有N 个数需要插入一棵空树中。给定插入序列,请在每个元素被插入
之后,输出所有节点的深度总和(根的深度为0)。 
【输入格式】 
输入的第一行一个整数n,表示序列长度。 
以下n行是序列中的数字,这些数字是各不相同的,在[1,n]区间。 
【输出格式】 
输出n行,第 i行整数表示第i个数插入树后,至这个节点的节点深度总和。

【样例输入】 









【样例输出】 





11 
13 
15 
【数据规模与约定】 
对于50%的数据,满足N ≤ 1000 
对于100%的数据,满足N ≤ 3 ∗ 1e5。


解析:

大概就是一道非常裸的二叉查找树的题??

但是如果直接暴力模拟的话会过不去,这是因为如果按顺序插入所有节点的时候复杂度会很高。

假设在插入节点x的时候我们已经有了一个树体,我们找比x小的最大节点设为u,比x大的最小节点设为v;

那么可以证明u和v一定是相连的,则若要插入x则要么为u的右节点,要么为v的左节点

即下图的两种情况

假设深度为depth[i],则depth[x]=max(depth[u],depth[v])+1;

现在问题变为了如何找u和v

答案就是:将输入序列倒过来维护,每当x找到了u/v,就把x删掉

具体做法:我们可以用双向链表来解决,当把所有的u、v都维护好后再正过来求总depth

代码

 

#include 
    
    
     
     

#define MAXN 300005

int n;
int a[MAXN];
int prev[MAXN], next[MAXN];
int u[MAXN], v[MAXN];
int deep[MAXN];

int main(void)
{

    scanf("%d", & n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", a + i);
        
    for (int i = 1; i <= n; ++i)
    {
        prev[i]  = i - 1;
        next[i] = i + 1;
    }

    int x;
    for (int i = n; i > 1; --i)
    {
        x = a[i];
        u[x] = prev[x];
        v[x] = next[x];
        next[prev[x]] = next[x];
        prev[next[x]] = prev[x];
    }

    long long sum = 0;
    for (int i = 1; i <= n; ++i)
    {
        x = a[i];
        
        if ((u[x] >= 1) && (u[x] <= n))
            if (deep[x] < deep[u[x]] + 1)
                deep[x] = deep[u[x]] + 1;
        if ((v[x] >= 1) && (v[x] <= n))
            if (deep[x] < deep[v[x]] + 1)
                deep[x] = deep[v[x]] + 1;
        
        sum += deep[a[i]];
        
        printf("%lld\n", sum);
    }

    return 0;
}
    
    

 

 赛 
【问题背景】 
zhx 举办了一场 OI 竞赛。
【问题描述】 
 n人参加信息学竞赛,共有 m道题。现在比赛已经结束,评分正在进行中,
对于已经结束评测的试题,已知每名考生这道题的答案是否正确,对于未结束评
测的试题,只知道每名考生是否ᨀ交答案。每个题分数固定,提交正确的答案的
考生可以得到这一题的分数,分数越高排名越靠前,分数相同编号小的考生排名
靠前。这 n 人中,排名最靠前的 s 人将获得入选代表队的资格,而这 s 个中将通
过最终的科学委员会面试选出其中的t个人。输入当前的评测信息(包括是否提
交,以及已经评测部分的是否正确)以及每道题的分值,问最终的 t人代表队共
有多少种组队的可能。 
【输入格式】 
 输入文件第一行是m,接下去 m行每行一个整数来表示每道题目的分值(整
数是正的表示该题已经评测,否则表示该题尚未评测,该题目的分值为这个整数
的绝对值)。然后是一个整数 n,接下去是一个 n 行 m 列的字母阵,字母阵只包
含 YN 两种字母(Yes or No),如果第 i 题已经完成评测,那么这个字母矩阵中
第 j 行第 i 列的表示第 j 名选手的第 i 题是否已经得分;如果第 i 题尚未完成评
测,那么这个字母矩阵中第 j 行第 i 列的表示第 j 名选手的第 i 题是否提交了解
答。最后两行两个数字,分别为s 和t。 
【输出格式】 
 输出文件只有一行,即为最终的t人代表队的组队方案数。 
【样例输入】 


-10 

NY 
YN 
YN 
YN 

2

【样例输出】 

【数据规模与约定】 
对于100%的数据, 满足1 ≤ N,M ≤ 50

解析:

我们先维护每个人所能得到的最大最小分值

其实也只有最大最小值有用

如果我们先选s个再选t个的话,答案一定会重(我在考场上是用的Hash判重,debug两个小时到爆炸)

因此我们选择直接枚举选入的t个人

设maxs[i],mins[i]非别为i号能得到的最多最少的分

给以maxs数组为第一关键字,mins为第二关键字排序,依次选择,假设i为可以进队的t人中 maxs最小的人

若前i个人中已经进了t人, 那么实际中有一些人是一定会进的->就是mins[x]>maxs[i]的那些人

上图中,假设有p[i]人的mins[x]>maxs[i]

假设前p[i]人中有k人进队,在(p[i]~i)范围内有t-k-1人进队,(假设i一定进队)

则判断条件应有:k<=t-1(第i人必须进队)、k<=p[i](显然。)、p[i]<s-1(必须要进的t人,t<s)、p[i]-k<=s-t(p[i]-k为前p[i]中进了s没进t的人数,s-t为总的进了s没进t的人)

答案即下图(实在不会打)

代码:

 

//mjy的标程
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                 
                   #include 
                  
                    #include 
                    using namespace std; #define MAXM 60 #define MAXN 60 #define DEBUG 0 typedef long int LINT; typedef long long INT64; typedef struct stProblem { int score; int state; } tProblem; tProblem problem[MAXM]; typedef struct stOIer { int id; LINT max; LINT min; } tOIer; tOIer oier[MAXN]; int p[MAXN]; int n, m, s, t; INT64 ans; INT64 calc_c(LINT m, LINT n) { if ((m < 0) || (n < 0)) return 0; if (m == 0) return 1; if (n == 0) return 0; if (m > n) return 0; if (m == n) return 1; INT64 result = 1; for (int i = 0; i < n - m; ++i) { result = result * (n - i) / (1 + i); } return result; } void bubble_sort() { int bubble_sort_cmp(tOIer *a, tOIer *b); for (int i = 0; i < n - 1; ++i) for(int j = n - 1; j > i; --j) if (bubble_sort_cmp(oier + (j - 1), oier + j) < 0) swap(oier[j - 1], oier[j]); // Defined in algorithm.h. } int bubble_sort_cmp(tOIer *a, tOIer *b) { if (a->max < b->max) { return -1; } else if (a->max == b->max) { return 0; if (a->id > b->id) return -1; else return 1; } else { return 1; } } int main(int argc, char *argv[]) { scanf("%d\n", & m); LINT temp_score; for (int i = 0; i < m; ++i) { scanf("%ld\n", & temp_score); if (temp_score > 0) { problem[i].score = temp_score; problem[i].state = 1; } else { problem[i].score = -temp_score; problem[i].state = 0; } } scanf("%d\n", & n); char temp_state[MAXM]; for (int i = 0; i < n; ++i) { oier[i].id = i; scanf("%s\n", temp_state); for (int j = 0; j < m; ++j) if (problem[j].state) { if ('Y' == temp_state[j]) { oier[i].max += problem[j].score; oier[i].min += problem[j].score; } } else { if ('Y' == temp_state[j]) oier[i].max += problem[j].score; } } scanf("%d\n", & s); scanf("%d\n", & t); bubble_sort(); // Max Score DESC Id ASC. for (int i = 0; i < n; ++i) for (int j = 0; j < i; p[i] += ((oier[j].min > oier[i].max) || ((oier[j].min == oier[i].max) && (oier[j].id < oier[i].id))), ++j) ; #if DEBUG for (int i = 0; i < n; ++i) printf("%d(ID %d) : MAX(%d), MIN(%d) P(%d)\n", i, oier[i].id, oier[i].max, oier[i].min, p[i]); printf("\n"); #endif int k_min, k_max; ans = 0; for (int i = t - 1; i < n; ++i) { if (p[i] > s - 1) break; k_min = max(p[i] - s + t, 0); k_max = min(p[i], t - 1); // t - 1 - k >= 0 #if DEBUG printf("STATE %d, MIN(%d), MAX(%d)\n", i, k_min, k_max); #endif for (int k = k_min; k <= k_max; ++k) ans += calc_c(k, p[i]) * calc_c(t - 1 - k, i - p[i]); } printf("%lld\n", ans); } 
                   
                  
                 
                
               
              
             
            
           
          
        
       
       
      
      
     
     
    
    

 

 卡 
【问题背景】 
zhx 是骨灰级炉石玩家。
【问题描述】 
zhx 买了36个卡包,并且把他们排列成6×6的阵型准备开包。左上角的包是
(0,0),右下角为(5,5)。为了能够开到更多的金色普通卡,zhx 会为每个包添加1−
5个玄学值,每个玄学值可以是1−30中的一个整数。但是不同的玄学值会造成
不同的欧气加成,具体如下: 
1、同一个卡包如果有两个相同的玄学值会有无限大的欧气加成。 
2、同一个卡包如果有两个相邻的玄学值会有A点欧气加成。 
3、相邻的两个卡包如果有相同的玄学值会有B点欧气加成。 
4、相邻的两个卡包如果有相邻的玄学值会有C点欧气加成。 
5、距离为2的卡包如果有相同的玄学值会有D点欧气加成。 
6、距离为2的卡包如果有相邻的玄学值会有E点欧气加成。 
以上的所有加成是每存在一个符合条件的就会加一次,如一包卡有1,2,3的玄
学值就会加两次。 
但是,玄学值是个不可控的东西,即使是 zhx 也只能自己决定
2,2 , 2,3 , 3,2 , (3,3)这几包卡的玄学值。为了能够抽到更多的金色普通卡,zhx
想知道自己能够获得的最少的欧气加成是多少。注意你只能修改玄学值,不能修
改玄学值的个数。 
【输入格式】 
输入的第一行有5个整数A,B,C,D,E。 
接下去有6×6的代表初始的玄学值。 
每个玄学值为[n:a1,a2,⋯,an]的᧿述形式。 
【输出格式】 
一行一个整数代表答案。 
【样例输入】 
5 4 3 2 1 
[1:1][1:2][1:3][1:4][1:5][1:6] 
[1:1][1:2][1:3][1:4][1:5][1:6] 
[1:1][1:2][5:1,2,3,4,5][5:1,2,3,4,5][1:5][1:6] 
[1:1][1:2][5:1,2,3,4,5][5:1,2,3,4,5][1:5][1:6] 
[1:1][1:2][1:3][1:4][1:5][1:6] 
[1:1][1:2][1:3][1:4][1:5][1:6] 

【样例输出】 
250 
【数据规模与约定】 
对于100%的数据,1 ≤ A,B,C,D,E ≤ 100,1 ≤ n≤ 5,1 ≤ ai≤ 30。有部分分。

注:距离为曼哈顿距离

解析:

一道考场上看不懂,听完题解一脸蒙蔽的题。(九维DP也是强,蒟蒻的我怕是明年也想不到)

曼哈顿距离:d=|x1-x2|+|y1-y2|;

实在是不想写QUQ反正考了我也不会写

就看看代码吧[逃]

//mjy的标程
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        

using namespace std;

#define now pre[a][b][c][d][e][s1][s2][s3][s4]
#define dis(a,b,c,d) (abs(a-c)+abs(b-d))

const int INF=0x3f3f3f3f;

int A,B,C,D,E,num[10][10],value[10][10][10],delta[10][10][40],dp[31][6][6][6][6][2][2][2][2];

char s[500];

bool map[6][6][6][6];

int main()
{
	freopen("card.in","r",stdin);
	freopen("card.out","w",stdout);

	scanf("%d%d%d%d%d",&A,&B,&C,&D,&E);
	for (int a=0;a<6;a++)
	{
		scanf("%s",s);
		int p=0;
		for (int b=0;b<6;b++)
		{
			int px=p;
			while (s[px]!=']')
				px++;
			p++;
			num[a][b]=s[p]-'0';
			p++;
			p++;
			for (int c=1;c<=num[a][b];c++)
			{
				int v=0;
				while (s[p]>='0' && s[p]<='9')
				{
					v=v*10+s[p]-'0';
					p++;
				}
				value[a][b][c]=v;
				p++;
			}
			p=px+1;
		}
	}
	int base=0;
	for (int a=0;a<6;a++)
		for (int b=0;b<6;b++)
			if (a>=2 && a<=3 && b>=2 && b<=3) ;
			else
			{
				sort(value[a][b]+1,value[a][b]+num[a][b]+1);
				for (int c=2;c<=num[a][b];c++)
					if (value[a][b][c]-value[a][b][c-1]==1) base+=A;
				for (int c=2;c<=3;c++)
					for (int d=2;d<=3;d++)
					{
						if (dis(a,b,c,d)==1)
						{
							for (int e=1;e<=num[a][b];e++)
							{
								delta[c][d][value[a][b][e]]+=B;
								delta[c][d][value[a][b][e]-1]+=C;
								delta[c][d][value[a][b][e]+1]+=C;
							}
						}
						if (dis(a,b,c,d)==2)
						{
							for (int e=1;e<=num[a][b];e++)
							{
								delta[c][d][value[a][b][e]]+=D;
								delta[c][d][value[a][b][e]-1]+=E;
								delta[c][d][value[a][b][e]+1]+=E;
							}
						}
					}
				for (int c=0;c<6;c++)
					for (int d=0;d<6;d++)
						if (dis(a,b,c,d)<=2 && (c!=a || d!=b) && !map[a][b][c][d])
						{
							map[a][b][c][d]=map[c][d][a][b]=true;
							if (c>=2 && c<=3 && d>=2 && d<=3) ;
							else
							{
								int dist=dis(a,b,c,d);
								for (int e=1;e<=num[a][b];e++)
									for (int f=1;f<=num[c][d];f++)
									{
										if (abs(value[a][b][e]-value[c][d][f])==0)
										{
											if (dist==1) base+=B;
											else base+=D;
										}
										if (abs(value[a][b][e]-value[c][d][f])==1)
										{
											if (dist==1) base+=C;
											else base+=E;
										}
									}
							}
						}
			}
	memset(dp,0x3f,sizeof(dp));
	dp[0][0][0][0][0][0][0][0][0]=base;
	for (int a=0;a<30;a++)
		for (int b=0;b<=num[2][2];b++)
			for (int c=0;c<=num[2][3];c++)
				for (int d=0;d<=num[3][2];d++)
					for (int e=0;e<=num[3][3];e++)
						for (int s1=0;s1<=1;s1++)
							for (int s2=0;s2<=1;s2++)
								for (int s3=0;s3<=1;s3++)
									for (int s4=0;s4<=1;s4++)
										if (dp[a][b][c][d][e][s1][s2][s3][s4]!=INF)
										{
											int v=dp[a][b][c][d][e][s1][s2][s3][s4];
											for (int sx1=0;sx1<=(b!=num[2][2]);sx1++)
												for (int sx2=0;sx2<=(c!=num[2][3]);sx2++)
													for (int sx3=0;sx3<=(d!=num[3][2]);sx3++)
														for (int sx4=0;sx4<=(e!=num[3][3]);sx4++)
														{
															int wmt=0;
															if (sx1) 
															{
																wmt+=delta[2][2][a+1];
																if (s1) wmt+=A;
																if (s2) wmt+=C;
																if (s3) wmt+=C;
																if (s4) wmt+=E;
															}
															if (sx2) 
															{
																wmt+=delta[2][3][a+1];
																if (s1) wmt+=C;
																if (s2) wmt+=A;
																if (s3) wmt+=E;
																if (s4) wmt+=C;
															}
															if (sx3) 
															{
																wmt+=delta[3][2][a+1];
																if (s1) wmt+=C;
																if (s2) wmt+=E;
																if (s3) wmt+=A;
																if (s4) wmt+=C;
															}
															if (sx4) 
															{
																wmt+=delta[3][3][a+1];
																if (s1) wmt+=E;
																if (s2) wmt+=C;
																if (s3) wmt+=C;
																if (s4) wmt+=A;
															}
															if (sx1 && sx2) wmt+=B;
															if (sx1 && sx3) wmt+=B;
															if (sx1 && sx4) wmt+=D;
															if (sx2 && sx3) wmt+=D;
															if (sx2 && sx4) wmt+=B;
															if (sx3 && sx4) wmt+=B;
															int &t=dp[a+1][b+sx1][c+sx2][d+sx3][e+sx4][sx1][sx2][sx3][sx4];
															if (t>v+wmt) t=v+wmt;
														}
										}
	int ans=INF;
	for (int a=0;a<=1;a++)
		for (int b=0;b<=1;b++)
			for (int c=0;c<=1;c++)
				for (int d=0;d<=1;d++)
					ans=min(ans,dp[30][num[2][2]][num[2][3]][num[3][2]][num[3][3]][a][b][c][d]);
	printf("%d\n",ans);

	return 0;
}
       
       
      
      
     
     
    
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值