D, Miku and Generals

缩点+背包

1、如果第一个人确定选择一个点,那么跟第一点有关系(有冲突)的所有的点都可以确定是第一个人还是第二个人的。

2、所以可以将每个联通块缩成一个点,这个点用两个值来表示,表示第一个人如果选了这个点,他将会得到,并且另一个人会得到。反之,第一个人得到,另一个人得到

(在这里记录一下最小获得的 val 

3、假设第一个人取了某个点最大的,那么他在这个点获得的“净val”就是他们的差值。

4、但是我们想让两个人的差值尽量小,所以我们对第一个人能得到的“净val”做一下01背包,就得到了第一个人可能得到的“净val”。

5、两个人差值尽量小,我们就希望第一个人得到的“净val”接近总的“净val”的一半,所以,我们可以从总的“净val”的一半开始往后找,如果找到了第一个人可以得到的“净val”的话,就相当于找到了使两个人差值最小的选择方法,然后答案就等于最大的“净val”加上上面求的最小获得的val。

#include <bits/stdc++.h>
#define Pair pair<int,int>
using namespace std;
int dp[505 * 505];
const int MAXN = 205;
vector<int>v[205];
int a[MAXN];
bool vis[205];
void dfs(int u, int& sum1, int& sum2, int bas)
{
	for (int i = 0; i < v[u].size(); i++)
	{
		int to = v[u][i];
		if (vis[to] == true)
			continue;
		vis[to] = true;
		if (bas > 0)
			sum1 += a[to];
		else
			sum2 += a[to];
		dfs(to, sum1, sum2, bas * -1);
	}
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int SUM = 0;
		vector<Pair>ans;
		memset(vis, false, sizeof(vis));
		memset(dp, 0, sizeof(dp));
		int n, m;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &a[i]);
			a[i] /= 100;
			SUM += a[i];
			v[i].clear();
		}
		while (m--)
		{
			int q, w;
			scanf("%d%d", &q, &w);
			v[q].push_back(w);
			v[w].push_back(q);
		}
		int sum = 0;
		for (int i = 1; i <= n; i++)
		{
			if (vis[i] == false)
			{
				int sol1 = a[i];
				int sol2 = 0;
				vis[i] = true;
				dfs(i, sol1, sol2, -1);
				ans.push_back(Pair(sol1, sol2));
				sum += min(sol1, sol2);
			}
		}
		dp[0] = 1;
		int cha = 0;
		for (int i = 0; i < ans.size(); i++)
		{
			int temp = abs(ans[i].first - ans[i].second);
			cha += temp;
			for (int j = SUM; j >= temp; j--)
			{
				if (dp[j - temp])
					dp[j] = 1;
			}
		}
		for (int i = cha / 2; i <= cha; i++)
		{
			if (dp[i])
			{
				printf("%d\n", (max(i, cha - i) + sum) * 100);
				break;
			}
		}
	}
}

C, Angel's Journey

几何,求切点(上板子

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <vector>
#include <algorithm>
const double eps = 1e-6;
const double PI = acos(-1.0); // PI = 180°= 3.14 
using namespace std;
//判断符号
int sign(double x)
{
	if (fabs(x) < eps)
		return 0;
	return x > 0 ? 1 : -1;
}

struct point
{
	double x;
	double y;
	point operator + (const point& other) { return point{ x + other.x, y + other.y }; }
	point operator - (const point& other) { return point{ x - other.x, y - other.y }; }
	point operator * (double k) { return point{ x * k, y * k }; }
	point operator / (double k) { return point{ x / k, y / k }; }
	bool operator == (const point& other)
	{
		return sign(x - other.x) == 0 && sign(y - other.y) == 0;
	}
	bool operator < (const point & other)
	{
		if (fabs(x - other.x) < eps)
			return y < other.y;
		return x < other.x;
	}
};
//点积
double dot(point a, point b)
{
	return a.x* b.x + a.y * b.y;
}
//叉积
double cross(point a, point b)
{
	return a.x* b.y - b.x * a.y;
}
//向量的长度 (可以计算两点距离
double getlen(point a)
{
	return sqrt(a.x * a.x + a.y * a.y);
}
//两向量夹角
double getangle(point a, point b)
{
	return acos(dot(a, b) / getlen(a) / getlen(b));
}
//点P 绕 点O 逆时针旋转angle角度(弧度制)
point rotate(point p, point o, double angle)
{
	point t = point{ p.x - o.x,p.y - o.y };
	double c = cos(angle), s = sin(angle);
	return point{ o.x + t.x * c - t.y * s,o.y + t.x * s + t.y * c };
}

struct circle
{
	point o;
	double r;
};
//经过点p,作圆o的切线,sol是切点数组,返回值为切点数
int gettangents(point p, circle oo, vector<point> & sol)
{
	double dis = getlen(p - oo.o);
	if (dis < oo.r)
		return 0;
	else if (sign(dis - oo.r) == 0)
	{
		sol.push_back(rotate(oo.o, p, PI / 2));
		return 1;
	}
	else
	{
		double angle = asin(oo.r / dis);
		sol.push_back(rotate(oo.o, p, angle));
		sol.push_back(rotate(oo.o, p, -angle));
		return 2;
	}
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		point a; circle o;
		scanf("%lf%lf%lf%lf%lf", &o.o.x, &o.o.y, &o.r, &a.x, &a.y);
		point l = point{ o.o.x - o.r,o.o.y }, r = point{ o.o.x + o.r,o.o.y };
		point b = point{ o.o.x,o.o.y - o.r };
		vector<point>sol;
		int cnt = gettangents(a, o, sol);
		double ans = 99999999;
		for (int i = 0; i < cnt; i++)
		{
			double dis = getlen(a - sol[i]);
			sol[i] = (sol[i] - a) * (sqrt(dis * dis - o.r * o.r)) / dis + a;
			if (sign(sol[i].y - o.o.y) < 0)
			{
				if (sign(sol[i].x - o.o.x) < 0)
					sol[i] = l;
				else
					sol[i] = r;
			}
			double res = getlen(a - sol[i]) + getangle(sol[i] - o.o, b - o.o) * o.r;
			ans = min(ans, res);
		}
		printf("%.4lf\n", ans);
	}
}