The 12th Zhejiang Provincial Collegiate Programming Contest -- 第七场选拔赛

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;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值