C:Convex Hull(ZOJ 3871)
题意:
给你n个点(n <= 1000),求所有子凸包的面积和的2倍。
思路:
我们先得知道一个求多边形面积的经典方法:
就是计算每个线段的有向面积:
比如说求上图的多边形的面积,我们直接计算每个线段的有向面积,例如求CD的有向面积。
直接计算S△PCD即可。(注意这个面积可正可负,计算方法是计算CD的叉积)
有了上述方法,我们便可解决这个题目。
既然是求凸包,我们就枚举每一条边,让这个边在凸包上,看看可以选择哪些点来构成凸包。
假如说我们可以选择x个点,那么我们就有C[X][1] + C[X][2] + ... + C[X][X]的方法来构成一个凸包,并且这个凸包包含这条边, 上述式子正好等于2^x - 1; 我们直接让 那条线段的有向面积 乘以这个次数即可, 有向面积就表示这个线段对这些凸包的面积的贡献。
注意:
可能大家觉的有些边会枚举两次,但是这两次不会算重,因为两次的方向不一样,选择凸包的点在这个线段的哪一侧就不一样。所以不会算重。
吐槽:
这个题因为自己计算叉积 取mod 的方式写错了 wa了一下午。
a-b 取模 不一定是(a-b + mod ) % mod;
有可能a-b的绝对值远远大于mod, 应该先取模在加模 在取模= =!
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int maxn = 1000 + 3;
const int mod = 998244353;
const double eps = 1e-10;
const double pi = 3.1415926535897932384626433832795023841971693993751058209749445923;
int dcmp(double a,double b){
if (fabs(a-b) < eps) return 0;
if (a > b) return 1;
return -1;
}
int T, n;
LL POW[maxn];
void init(){
POW[0] = 1;
for (int i = 1; i < maxn; ++i){
POW[i] = (POW[i-1] * 2) % mod;
}
}
struct Node{
LL x,y;
void read(){
scanf("%lld %lld",&x, &y);
}
double ang;
bool operator < (const Node& rhs) const {
return dcmp(ang, rhs.ang) == -1;
}
}p[maxn<<1],p2[maxn<<1];
LL get(Node a,Node b){
return (((a.x * b.y - b.x * a.y) % mod) + mod) % mod;
}
void add(LL& ans,LL v){
ans += v;
if (ans >= mod) ans -= mod;
}
LL solve(int id){
for (int i = 0; i < n; ++i){
if (i != id)p[i].ang = atan2(p[i].y-p[id].y, p[i].x - p[id].x);
}
swap(p[0], p[id]);
sort(p+1, p+n);
for (int i = 0; i < n; ++i){
p[n+i] = p[i+1];
p[n+i].ang += 2*pi;
}
p[2*n].ang = 1e18;
LL ans = 0LL;
int j = 1;
for (int i = 1; i < n; ++i){
while(dcmp(p[j+1].ang - p[i].ang, pi) == -1) ++j;
add(ans, (POW[j-i] * get(p[0],p[i])) % mod );
}
return ans;
}
int main(){
init();
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for (int i = 0; i < n; ++i){
p[i].read();
p2[i] = p[i];
}
LL ans = 0;
for (int i = 0; i < n; ++i){
for (int j = 0; j < n; ++j){
p[j] = p2[j];
}
add(ans,solve(i));
}
printf("%lld\n",ans);
}
return 0;
}
I:Earthstone Keeper(ZOJ 3877)
题意:
给你一个n*m的迷宫,你要从起点走到终点,每个格子有怪兽和夹子和墙,墙不能走,夹子踩上去会受到伤害,但是踩完之后夹子会刷新好, 你走到怪兽旁边,怪兽会对你造成伤害,同样你会杀死怪兽,不会复活。要求路途 受到的伤害最小,伤害相同保证距离最小。
思路:
本以为是一个搜索,上了类似dijkstra,可能Node 结点里存的太多了 一直TLE.
应该是建图求最短路。
一开始想的是 :
假如A到B相邻的话,那么A到B连边,边权为B受到的伤害 减去 A和B 相邻怪兽的伤害。
这样是不对的。
因为这样不能保证 怪兽死后不复活。
例如:
##.
#A.
. . C
所以在加一种情况:
对于每一种怪兽来说,他相邻的点 肯定没有怪兽 假如说是AB两点把。
直接按照第一种方式连即可, 只不过距离权值是2.
吐槽:
太卡内存了= =
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 500 + 3;
const int maxm = maxn * maxn;
int sx,sy,ex,ey, T;
int n,m;
char s[maxn][maxn];
bool vis[maxm];
vector<int>g[maxm];
const int dx[] = {1,-1,0,0,0};
const int dy[] = {0,0,1,-1,0};
struct Edge{
int from,to,f,d;
Edge(int from = 0,int to = 0,int f = 0,int d = 0):from(from),to(to),f(f),d(d){}
};
vector<Edge>edges;
struct Node{
int u,f,d;
Node(int u = 0, int f = 0,int d = 0):u(u),f(f),d(d){}
bool operator < (const Node& rhs) const {
return f > rhs.f || (f == rhs.f && d > rhs.d);
}
};
int code(int i,int j){
return (i-1) * m + j;
}
void addEdge(int from,int to,int ff,int dis){
g[from].push_back((int)edges.size());
edges.push_back(Edge(from,to,ff,dis));
}
priority_queue<Node>q;
struct node{
int f,d,fa;
}ans[maxm];
void Dijkstra(){
while(!q.empty()) q.pop();
memset(vis,0,sizeof vis);
q.push(Node(code(sx,sy),0,0));
ans[code(sx,sy)].f = ans[code(sx,sy)].d = 0;
ans[code(sx,sy)].fa = -1;
while(!q.empty()){
Node nod = q.top(); q.pop();
int u = nod.u, f = nod.f, d = nod.d;
if (vis[u]) continue;
vis[u] = 1;
for (int i = 0; i < g[u].size(); ++i){
Edge& e = edges[ g[u][i] ];
if (ans[e.to ].f > ans[u].f + e.f){
ans[e.to].f = ans[u].f + e.f;
ans[e.to].d = ans[u].d + e.d;
ans[e.to].fa = u;
q.push(Node(e.to, ans[e.to].f, ans[e.to].d));
}
else if (ans[e.to ].f == ans[u].f + e.f){
if (ans[e.to].d > ans[u].d + e.d){
ans[e.to].d = ans[u].d + e.d;
ans[e.to].fa = u;
q.push(Node(e.to, ans[e.to].f, ans[e.to].d));
}
}
}
}
}
inline bool init(int xx,int yy){
return xx >= 1 && xx <= n && yy >= 1 && yy <= m && s[xx][yy ] != '#';
}
int aaa(int x){
if (x < 0) return -x;
return x;
}
void add(int x,int y){
if (s[x][y] == '#') return;
if (!isupper(s[x][y])){
for (int i = 0; i < 4; ++i){
int xx = dx[i] + x;
int yy = dy[i] + y;
if (init(xx,yy) && s[xx][yy] != '#'){
int sum = 0;
for (int j = 0; j < 5; ++j){
int xxx = dx[j] + xx;
int yyy = dy[j] + yy;
if (init(xxx,yyy) && isupper(s[xxx][yyy]) && aaa(xxx-x) + aaa(yyy-y) > 1){
sum += s[xxx][yyy] - 'A' + 1;
}
}
if (islower(s[xx][yy])){
sum += s[xx][yy] - 'a' + 1;
}
// printf("%d %d %d\n",code(x, y),code(xx,yy),sum);
addEdge(code(x,y), code(xx,yy), sum, 1);
}
}
}
else {
for (int i = 0; i < 4; ++i){
int xx = dx[i] + x;
int yy = dy[i] + y;
if (init(xx,yy)){
for (int j = 0; j < 4; ++j){
if (i == j) continue;
int xxx = dx[j] + x;
int yyy = dy[j] + y;
if (init(xxx,yyy)){
int sum = 0;
for (int k = 0; k < 4; ++k){
int xxxx = dx[k] + xxx;
int yyyy = dy[k] + yyy;
if (init(xxxx,yyyy) && isupper(s[xxxx][yyyy]) && aaa(xxxx-xx) + aaa(yyyy-yy) > 1){
sum += s[xxxx][yyyy] - 'A' + 1;
}
}
if (islower(s[xxx][yyy]))sum += s[xxx][yyy] - 'a' + 1;
addEdge(code(xx,yy),code(xxx,yyy),sum,2);
// printf("%d %d %d %d\n", code(xx,yy),code(xxx,yyy),sum,2);
}
}
}
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d %d",&n, &m);
scanf("%d%d%d%d%*c",&sx, &sy, &ex, &ey);
edges.clear();
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= m; ++j){
g[code(i,j)].clear();
ans[code(i,j)].d = ans[code(i,j)].f = 0x3f3f3f3f;
s[i][j] = getchar();
}
getchar();
}
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= m; ++j){
add(i,j);
}
}
Dijkstra();
// int cur = code(ex,ey);
// while(~cur){
// printf("%d-",cur);
// cur = ans[cur].fa;
// }
// puts("");
printf("%d %d\n",ans[code(ex,ey) ].f, ans[code(ex,ey) ].d);
}
return 0;
}