• 9.27下午考试(Nescafé 29杯模拟赛)


    140pts(100+30+10)Rank3

    前几天还考了一场,AK,没什么好总结的,所以就没写博客。

    炸:

    T2,模拟退火突然不会了,写个状压dp,排序边的时候sort的N而不是M

    这个坑经常出!!

    T3,打表没有打全,规律推的有问题,本来应该加到5,我却加到了2。

    这个注意着点就行了

    我的变量名又开始各种鬼畜了。。。

    由于教练让我写题解了,所以题解就搁下面了

    rainbow

    题目大意:笛卡尔坐标系中,x轴非负半轴上有7个在第一象限半圆,你需要把这7个半圆的半径增加一个相同的实数,使得这些半圆恰好覆盖线段(y=h(0le xle x_0))

    题解:由于答案是单调的(就是如果一个(r_0)是合法的答案,那么(forall rge r_0)都有(r)是一个合法的答案)

    所以可以通过二分答案来解决,二分这个(r),然后算出所有的圆覆盖直线(y=h)的区域(一定是一个线段),然后跑一遍区间求并即可。如果一个圆和线段相离,把这个区间设为([-1,-1])即可(或者是别的奇奇怪怪的值都行)。注意不要计算两个圆之间的交点,不好写,有误差还容易写错

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #define six 6
    #define seven 7
    #define eight 8
    #define hexo 1e-9//鬼畜变量名常量名系列程序之Nescafe29
    using namespace std;
    
    struct fuck
    {
    	double x, r;
    }a[10];
    
    struct pdf
    {
    	double l, r;
    }s[10];
    
    double h, x0;
    
    bool ghj1222(const pdf &a, const pdf &b)
    {
    	if (fabs(a.l - b.l) <= hexo)
    		return a.r < b.r;
    	return a.l < b.l;
    }
    
    bool valid(double ass)
    {
    	//然后就是区间覆盖问题了你特么求交点干什么
    	//233333
    	for (int i = 1; i <= seven; i++)
    	{
    		double qtmd = (a[i].r + ass) * (a[i].r + ass) - h * h;
    		if (qtmd <= 0)
    			s[i].l = s[i].r = 123456789;
    		else
    		{
    			qtmd = sqrt(qtmd);
    			s[i].l = a[i].x - qtmd;
    			s[i].r = a[i].x + qtmd;
    		}
    	}
    	sort(s + 1, s + eight, ghj1222);
    	double l = s[1].l, r = s[1].r;
    	if (l > 0)
    		return false;
    	for (int i = 2; i <= seven; i++)
    	{
    		if (r >= x0)
    			return true;
    		if (s[i].l <= r)
    			r = max(s[i].r, r);
    		else
    			return false;
    	}
    	return r >= x0;
    }
    
    int main()
    {
    	freopen("rainbow.in", "r", stdin);
    	freopen("rainbow.out", "w", stdout);
    	scanf("%lf%lf", &h, &x0);
    	for (int i = 1; i <= seven; i++)
    		scanf("%lf%lf", &a[i].x, &a[i].r);
    	double l = 0;
    	double r = 12345;//Very Safe!!!..........2333333qtmd
    	while (r - l > hexo)
    	{
    		double mid = (l + r) / 2;
    		if (valid(mid))
    			r = mid;
    		else
    			l = mid;
    	}
    	printf("%.2f
    ", l);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    clover

    题目大意:给定一张图,求一个图的分量使得这个分量内所有联通块的点权和为0,且最小化边权。

    题解:本题可以随机化、模拟退火做,也可以状压dp

    状压dp:由于(Nle16)所以可以状压dp做。令(f[i])代表到达状态为i的最小花费,其中(i)的二进制第(j)位为1,则第(j)个点已经被选择,否则未被选择。首先(f[0]=0)(Ans=f[2^N-1])。由于是最小化某个数,所以f数组先初始化为极大值。考虑转移:首先一个转移肯定是往原状态里加一个连通块组成一个新状态,所以我们预处理出所有的连通块,保证这个连通块内所有的点和为0,且所有点之间仅由“属于这个连通块的边”联通。我们枚举所有的状态,先判断这个状态表示的点集内所有点权和是否为0,如果为0,那么在这个点集内跑一遍最小生成树。注意先把所有边sort一下,然后只对于起点和终点都属于这个点集内的边更新并查集,否则不去更新。最后处理出来最小生成树要检查一下这个点集内所有的点是否联通(也就是所有的点的父亲是否都是一个节点),如果是就返回最小生成树的边权即可,不是要返回这个转移不合法。记录所有合法的转移(记录点集和花费)。先枚举所有状态,然后枚举所有转移(因为一个状态一定是由一个编号比它小的状态转移过来的),(这里使用填表法)如果这个转移能够由某一个状态转移到这个状态(用二进制xjb判一下就行了)那么就计算出这个状态并转移即可。最后如果答案没有被更新,那么就是Impossible,否则输出答案即可。你也可以预先搜一下是否有解,有解条件是所有强连通分量内点权和都为0

    模拟退火:下面是来自GMPotlc的模拟退火算法,请大家认真欣赏一下。

    T2 : 正经(xjb)算法
    根据题意 如果 x 点是可行的 那么我们只需要 让他们联通即可 那么我们就可以联想到
    最小生成树 , 然而事实 总是那么不尽如人意 , duipai 发现 结果几乎全是错的
    仔细思考发现 有这样一个 error :
    对于 两个 联通块 如果各自联通块的权值和 分别已经 为零了 那么我们就不在需要
    去将这两个联通块 连在一起 , 怎么解决这个问题呢 ?
    聪明的人 都已经想到了 xjb 算法
    我们 对于 克鲁斯卡尔 加边的时候 我们 xjb rand()即可
    然后 进行 300000 次即可 (当然更聪明的人 其实可以xjb 退火了)

    这是我的状压dp

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    int N, M, tot, g[20][20], val[20];
    //第0位为第1个,第1位为第2个,最高位为第N个
    
    struct edge
    {
    	int u, v, w;
    }a[200];
    
    namespace Confusion
    {
    	
    	int st[65600], cost[65600], top, fa[20], f[65600];
    	
    	int getf(int x)
    	{
    		return fa[x] == x ? x : fa[x] = getf(fa[x]);
    	}
    	
    	bool cmp(const edge &a, const edge &b)
    	{
    		return a.w < b.w;
    	}
    	
    	bool valid1(int fuck)
    	{
    		int ans = 0;
    		for (int i = 1; i <= N; i++, fuck >>= 1)
    			if (fuck & 1)
    				ans += val[i];
    		return ans == 0;
    	}
    	//表表表表表表表表表表表表表表表表表表表表表
    	int valid2(int fuck)
    	{
    		bool v[20];
    		
    		memset(v, 0, sizeof(v));
    		
    		for (int i = 1; i <= N; i++, fuck >>= 1)
    			if (fuck & 1)
    			{
    				v[i] = true;
    				fa[i] = i;
    			}
    		int ans = 0;
    		//说好的克鲁斯卡尔呢我为什么存的是邻接矩阵??!?!!??!?!?!?!?!?!?!?!?!?
    		//好了写了个边表
    		for (int i = 1; i <= M; i++)
    		{
    			if (v[a[i].u] == true && v[a[i].v] == true)
    			{
    				int x = getf(a[i].u), y = getf(a[i].v);
    				if (x != y)
    				{
    					fa[x] = y;
    					ans += a[i].w;
    				}
    			}
    		}
    		int common_father = -1;
    		for (int i = 1; i <= N; i++)
    			if (v[i] == true)
    			{
    				if (common_father == -1 || getf(i) == common_father)
    					common_father = getf(i);
    				else
    					return -1;
    			}
    		return ans;
    	}
    	
    	int work()
    	{
    		sort (a + 1, a + 1 + M, Confusion::cmp);
    		tot = (1 << N) - 1;
    		for (int i = 1; i <= tot; i++)
    			if (valid1(i))
    			{
    				int res = valid2(i);
    				if (res >= 0)
    				{
    					st[++top] = i;
    					cost[top] = res;
    				}
    			}
    		memset(f, 0x3f, sizeof(f));
    		f[0] = 0;
    		for (int i = 1; i <= tot; i++)
    		{
    			for (int j = 1; j <= top; j++)
    			{
    				if ((i ^ st[j]) + st[j] == i)
    					f[i] = min(f[i], f[i ^ st[j]] + cost[j]);
    			}
    		}
    		return f[tot];
    	}
    }
    
    namespace Impossible
    {
    	bool v[20];
    	int search(int x)
    	{
    		int ans = val[x];
    		v[x] = true;
    		for (int i = 1; i <= N; i++)
    			if (g[x][i] < 0x3f3f3f3f && v[i] == false)
    				ans += search(i);
    		return ans;
    	}
    	bool judge()
    	{
    		memset(v, 0, sizeof(v));
    		for (int i = 1; i <= N; i++)
    			if (v[i] == false)
    				if (search(i) != 0)
    					return false;
    		return true;
    	}
    }
    
    int main()
    {
    	freopen("clover.in", "r", stdin);
    	freopen("clover.out", "w", stdout);
    	scanf("%d%d", &N, &M);
    	for (int i = 1; i <= N; i++)
    		scanf("%d", &val[i]);
    	memset(g, 0x3f, sizeof(g));
    	for (int i = 1; i <= N; i++)
    		g[i][i] = 0;
    	for (int x, y, z, i = 1; i <= M; i++)
    	{
    		scanf("%d%d%d", &x, &y, &z);
    		x++;
    		y++;
    		a[i] = (edge){x, y, z};
    		g[x][y] = min(g[x][y], z);
    		g[y][x] = min(g[y][x], z);
    	}
    	if (Impossible::judge() == 0)
    		puts("Impossible");
    	else
    		printf("%d
    ", Confusion::work());
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
    

    domine

    题目大意:询问(N)个节点的AVL树的形状数。(Nle 3000)

    题解:我们不难想到一个dp的做法:令(f[i][j])代表有i个节点的树的高度为(j)的方案数。不难写出转移方程为:

    (displaystyle f[i][j]=sum_{x+y=i-1}(f[x][j-1]*f[y][j-1]+f[x][j-2]*f[y][j-1]+f[x][j-1]*f[y][j-2]))

    枚举左子树的节点数为x,右子树的节点数为y,由于整棵树的高度为j,所以一定有一个子树高度为j-1,由于两个子树高度最多差1,所以另一个子树高度可以为j-1或j-2。所以有3种方案:左j-1右j-1,左j-2右j-1,左j-1右j-2。根据乘法原理,整棵树数量为两棵子树的数量相乘,根据加法原理,上面3中方案相加即可。

    由于时间复杂度为(O(n^3)),所以这里需要稍微优化一下。首先由于树不可能是一个链,我们先假设j枚举到30即可。把f数组打表之后发现j是随着i变化而变化,大概j是i的一个对数函数。每一个i对应存在(f[i][j])长度区间最长为5,且每一个i存在第一个有数的j是i的二进制最高位的位置。所以暴力求i最高位即可,然后向后枚举5位即可。这个能优化到(O(n^2))乘以一个常数,虽然比较慢,但是能卡进去。

    还有这道题有一个小坑:题目并没有让你取模(10^9)输出,而是保留后9位。就是后9位有前导0也要输出。我们手动模拟发现,当(nge38)时候有(Ansge10^9),所以判一下,如果(n<38)就直接输出,(nge38)就要恰好保留9位,保留前导0。如果使用printf函数输出整数就用%09lld%09d即可保留前导0。

    另外这题的答案是一个整数数列,在OEIS的编号为A006265,点击超链接即可查看有关信息:N个节点的高度平衡的AVL树的形状。

    #include <iostream>
    #include <cstdio>
    #include <iomanip>
    #define asshole 1000000000
    using namespace std;
    
    //由N个节点组成的高度为M的树的方案数(高度算上根节点)
    long long f[3010][3010], ans[3010];
    #define N 3000
    
    int highbit(int x)
    {
    	int ans = 0;
    	while (x > 0)
    	{
    		x >>= 1;
    		ans++;
    	}
    	return ans;
    }
    
    //orz applepi!!!
    //#include <ctime>
    int main()
    {
    //	int fuckyou = clock();
    //	freopen("domine.in", "r", stdin);
    //	freopen("domine.out", "w", stdout);
    	f[0][0] = 1;
    	f[1][1] = 1;
    	ans[1] = 1;
    	for (int i = 2; i <= N; i++)
    	{
    		int fuck = highbit(i);
    		for (int j = fuck; j <= fuck + 4; j++)//展现出填表法的威力吧
    		{
    			for (int x = 0; x < i; x++)
    			{
    				int y = i - 1 - x;
    				f[i][j] = ((f[i][j] + f[x][j - 1] * f[y][j - 1] % asshole) % asshole + (f[x][j - 2] * f[y][j - 1] % asshole + f[x][j - 1] * f[y][j - 2] % asshole) % asshole) % asshole;
    			}
    			(ans[i] += f[i][j]) %= asshole;
    		}
    	}
    	int fuck = 2333;
    	while (2333)
    	{
    		scanf("%d", &fuck);
    		if (fuck == 0)
    			break;
    		if (fuck >= 38)
    			printf("%09lld
    ", ans[fuck]);
    		else
    			printf("%lld
    ", ans[fuck]);
    	}//WAWA Lian
    //	fprintf(stderr, "%d
    ", clock() - fuckyou);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    个人关于浮动的理解
    css课堂笔记(盒子模型,标准文档流,浮动,美化)
    css和html课堂笔记
    html中的行内元素和块级元素
    css简介及常用语法
    html简介及常用功能
    权重比较
    盒模型
    css常见内联和块级元素
    Vigenère密码
  • 原文地址:https://www.cnblogs.com/oier/p/9715640.html
Copyright © 2020-2023  润新知