• Regionals 2012 :: HangZhou


    题目传送门
    排行榜

    一个人做了12年北大出的题,自己还是太弱了,图论的知识忘光光,最小生成树裸题写不来,Dijkstra TLE不知道用SPFA。

    简单几何(点到线段的距离) + 三分 B Stealing a Cake

    题意:圆外一个点先到圆再到矩形的最短距离。

    分析:由于圆在[0, PI]和[PI, PI*2]上是单峰函数,用三分求极值,应该在[0,PI*2]上也算单峰函数吧。

    /************************************************
    * Author        :Running_Time
    * Created Time  :2015/11/7 星期六 17:24:32
    * File Name     :B_2.cpp
     ************************************************/
    
    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <sstream>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <vector>
    #include <queue>
    #include <deque>
    #include <stack>
    #include <list>
    #include <map>
    #include <set>
    #include <bitset>
    #include <cstdlib>
    #include <ctime>
    using namespace std;
    
    #define lson l, mid, rt << 1
    #define rson mid + 1, r, rt << 1 | 1
    typedef long long ll;
    const int N = 1e5 + 10;
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e9 + 7;
    const double EPS = 1e-10;
    const double PI = acos (-1.0);
    int dcmp(double x)  {       //三态函数,减少精度问题
        if (fabs (x) < EPS) return 0;
        else    return x < 0 ? -1 : 1;
    }
    struct Point    {       //点的定义
        double x, y;
        Point () {}
        Point (double x, double y) : x (x), y (y) {}
        Point operator + (const Point &r) const {       //向量加法
            return Point (x + r.x, y + r.y);
        }
        Point operator - (const Point &r) const {       //向量减法
            return Point (x - r.x, y - r.y);
        }
        Point operator * (double p) const {       //向量乘以标量
            return Point (x * p, y * p);
        }
        Point operator / (double p) const {       //向量除以标量
            return Point (x / p, y / p);
        }
        bool operator < (const Point &r) const {       //点的坐标排序
            return x < r.x || (x == r.x && y < r.y);
        }
        bool operator == (const Point &r) const {       //判断同一个点
            return dcmp (x - r.x) == 0 && dcmp (y - r.y) == 0;
        }
    };
    typedef Point Vector;       //向量的定义
    Point read_point(void)   {      //点的读入
        double x, y;
        scanf ("%lf%lf", &x, &y);
        return Point (x, y);
    }
    double dot(Vector A, Vector B)  {       //向量点积
        return A.x * B.x + A.y * B.y;
    }
    double cross(Vector A, Vector B)    {       //向量叉积
        return A.x * B.y - A.y * B.x;
    }
    Vector rotate(Vector A, double rad) {
        return Vector (A.x * cos (rad) - A.y * sin (rad), A.x * sin (rad) + A.y * cos (rad));
    }
    double length(Vector A) {
        return sqrt (dot (A, A));
    }
    double angle(Vector A, Vector B)    {       //向量转角,逆时针,点积
        return acos (dot (A, B) / length (A) / length (B));
    }
    double point_to_seg(Point p, Point a, Point b)  {
        if (a == b) return length (p - a);
        Vector V1 = b - a, V2 = p - a, V3 = p - b;
        if (dcmp (dot (V1, V2)) < 0)    return length (V2);
        else if (dcmp (dot (V1, V3)) > 0)   return length (V3);
        else    return fabs (cross (V1, V2)) / length (V1);
    }
    
    struct Circle   {
        Point c;
        double r;
        Circle () {}
        Circle (Point c, double r) : c (c), r (r) {}
        Point point(double a)   {
            return Point (c.x + cos (a) * r, c.y + sin (a) * r);
        }
    };
    
    Point s, a, b, c, d;
    Circle C;
    
    double cal_dis(double rad)  {
        Point p = C.point (rad);
        double dis1 = length (s - p);
        double dis2 = 1e9;
        dis2 = min (dis2, point_to_seg (p, a, b));
        dis2 = min (dis2, point_to_seg (p, b, c));
        dis2 = min (dis2, point_to_seg (p, c, d));
        dis2 = min (dis2, point_to_seg (p, d, a));
        return dis1 + dis2;
    }
    
    int main(void)    {
        double x, y, r, x2, y2;
        while (scanf ("%lf%lf", &x, &y) == 2)	{
    		if (dcmp (x) == 0 && dcmp (y) == 0)	break;
    		s = Point (x, y);
    		scanf ("%lf%lf%lf", &x, &y, &r);
    		C = Circle (Circle (Point (x, y), r));
            scanf ("%lf%lf%lf%lf", &x, &y, &x2, &y2);
            a = Point (min (x, x2), max (y, y2));
            b = Point (min (x, x2), min (y, y2));
            c = Point (max (x, x2), min (y, y2));
            d = Point (max (x, x2), max (y, y2));
            Vector V[2];
            int cnt = point_cir_tan (s, C, V);
            double l = angle (Vector (1, 0), V[0]), r = angle (Vector (1, 0), V[1]);
            if (l > r)  swap (l, r);
            while (r - l > EPS) {
                double mid = (l + r) / 2;
                double lmid = (l + mid) / 2;
                double rmid = (r + mid) / 2;
                double res1 = cal_dis (lmid),
                       res2 = cal_dis (rmid);
                if (res1 < res2)    {
                    r = rmid;
                }
                else    l = lmid;
            }
            printf ("%.2f
    ", cal_dis (l));
        }
        return 0;
    }
    

    预处理+递推DP C Substrings

    题意:给n个数字,q次询问,每次回答所有长度为w的子串不同数字个数的和

    分析:先想状态,dp[w]表示长度为w的不同数字总和,有个递推关系,dp[i] = dp[i-1] - last[i-1] + sum[i]。last[i]表示后缀长度为i的不同个数,从i-1到i时能发现少了最后一个长度i-1的子串,当然还要加上新的权值,sum[i]表示两个相同数字距离大于等于i的所有个数,那么先求c[i] 等于i的所有个数,后缀累加就行了。这题充分使用了预处理的技巧求解问题,复杂度 O (n)

    /************************************************
    * Author        :Running_Time
    * Created Time  :2015/11/9 星期一 10:23:58
    * File Name     :C.cpp
     ************************************************/
    
    #include <bits/stdc++.h>
    using namespace std;
    
    #define lson l, mid, rt << 1
    #define rson mid + 1, r, rt << 1 | 1
    typedef long long ll;
    const int N = 1e6 + 10;
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e9 + 7;
    const double EPS = 1e-10;
    const double PI = acos (-1.0);
    ll dp[N];
    int a[N], pre[N], sum[N], last[N], c[N];
    
    int main(void)    {
        int n, q;
        while (scanf ("%d", &n) == 1)   {
            if (!n) break;
            for (int i=1; i<=n; ++i)    scanf ("%d", &a[i]);
            memset (c, 0, sizeof (c));
            memset (pre, 0, sizeof (pre));
            for (int i=1; i<=n; ++i)    {
                c[i-pre[a[i]]]++;
                pre[a[i]] = i;
            }
            sum[n] = c[n];
            for (int i=n-1; i>=1; --i)  {
                sum[i] = sum[i+1] + c[i];
            }
            memset (c, 0, sizeof (c));
            last[1] = 1; c[a[n]]++;
            for (int i=2; i<=n; ++i)    {       //length
                if (c[a[n-i+1]] == 0)   {
                    last[i] = last[i-1] + 1;
                    c[a[n-i+1]] = 1;
                }
                else    last[i] = last[i-1];
            }
            dp[1] = n;
            for (int i=2; i<=n; ++i)    {
                dp[i] = dp[i-1] - last[i-1] + sum[i];
            }
            scanf ("%d", &q);
            while (q--) {
                int w;  scanf ("%d", &w);
                printf ("%I64d
    ", dp[w]);
            }
        }
    
       //cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.
    ";
    
        return 0;
    }

    SPFA/BFS H Friend Chains

    题意:就是求任意两点的最短路的最大值

    分析:Floyd O(n^3)绝对超时,我想了Dijkstra,O (V * (VlogE + V),后来用了SPFA倒是勉勉强强过了,对Dijkstra无爱了。杭电discuss里有人写了最快的解法,类似于求树的直径,两边BFS。问题是第一次找最远的点可能有多个,要选择度数最小的,否则度数多了,从那个点出发到某些点并不是最远的,也就不是直径。

    SPFA

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e3 + 10;
    const int E = 2e4 + 10;
    const int INF = 0x3f3f3f3f;
    struct Edge	{
    	int v, w, nex;
    	Edge () {}
        Edge (int v, double w, int nex) : v (v), w (w), nex (nex) {}
    }edge[E];
    int head[N];
    bool vis[N];
    int d[N];
    int n, m, e;
    
    void init(void)	{
    	memset (head, -1, sizeof (head));
    	e = 0;
    }
    void add_edge(int u, int v, int w)	{
        edge[e] = Edge (v, w, head[u]);
        head[u] = e++;
    }
    
    void SPFA(int s)    {
        for (int i=1; i<=n; ++i)    {
            vis[i] = false; d[i] = INF;
        }
        vis[s] = true;  d[s] = 0;
        queue<int> Q;   Q.push (s);
        while (!Q.empty ()) {
            int u = Q.front (); Q.pop ();
            vis[u] = false;
            for (int i=head[u]; ~i; i=edge[i].nex)  {
                int v = edge[i].v, w = edge[i].w;
                if (d[v] > d[u] + w)    {
                    d[v] = d[u] + w;
                    if (!vis[v])    {
                        vis[v] = true;  Q.push (v);
                    }
                }
            }
        }
    }
    
    map<string, int> id;
    char str1[11], str2[11];
    
    int main(void)	{
    	while (scanf ("%d", &n) == 1)	{
    		if (!n)	break;
    		id.clear ();
    		for (int i=1; i<=n; ++i)	{
                scanf ("%s", &str1);
                id[str1] = i;
    		}
    		init ();
    		scanf ("%d", &m);
    		for (int i=1; i<=m; ++i)	{
    			scanf ("%s%s", &str1, &str2);
                if (id[str1] == id[str2])	continue;
    			add_edge (id[str1], id[str2], 1);
    			add_edge (id[str2], id[str1], 1);
    		}
    
    		int ans = 0;
    		for (int i=1; i<=n; ++i)	{
    		    SPFA (i);
                for (int j=1; j<=n; ++j)    {
                    if (d[j] == INF)    {
                        ans = -1;   break;
                    }
                    if (ans < d[j]) ans = d[j];
                }
                if (ans == -1)  break;
            }
    		printf ("%d
    ", ans);
    	}
    
    	return 0;
    }
    

    BFS

    /************************************************
    * Author        :Running_Time
    * Created Time  :2015/11/7 星期六 16:16:25
    * File Name     :H_2.cpp
     ************************************************/
    
    #include <bits/stdc++.h>
    using namespace std;
    
    #define lson l, mid, rt << 1
    #define rson mid + 1, r, rt << 1 | 1
    typedef long long ll;
    const int N = 1e3 + 10;
    const int E = 2e4 + 10;
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e9 + 7;
    const double EPS = 1e-10;
    const double PI = acos (-1.0);
    struct Edge	{
    	int v, w, nex;
    	Edge () {}
        Edge (int v, int w, int nex) : v (v), w (w), nex (nex) {}
    }edge[E];
    int head[N], d[N], deg[N];
    bool vis[N];
    int n, m, e;
    
    void init(void)	{
    	memset (head, -1, sizeof (head));
        memset (deg, 0, sizeof (deg));
    	e = 0;
    }
    void add_edge(int u, int v, int w)	{
        edge[e] = Edge (v, w, head[u]);
        head[u] = e++;
    }
    
    int BFS(int s) {
        for (int i=1; i<=n; ++i)    {
            d[i] = (i == s) ? 0 : INF;
            vis[i] = (i == s) ? true : false;
        }
        queue<int> Q;   Q.push (s);
        int idx = 0, mx = 0;
        while (!Q.empty ()) {
            int u = Q.front (); Q.pop ();
            for (int i=head[u]; ~i; i=edge[i].nex)  {
                int v = edge[i].v, w = edge[i].w;
                if (vis[v]) continue;
                if (d[v] > d[u] + w)    {
                    d[v] = d[u] + w;
                    vis[v] = true;  Q.push (v);
                }
                if (d[v] > mx || (d[v] == mx && deg[v] < deg[idx]))  {
                    mx = d[v];  idx = v;
                }
            }
        }
        return idx;
    }
    map<string, int> id;
    char str1[11], str2[11];
    
    int main(void)	{
    	while (scanf ("%d", &n) == 1)	{
    		if (!n)	break;
    		id.clear ();
    		for (int i=1; i<=n; ++i)	{
                scanf ("%s", &str1);
                id[str1] = i;
    		}
    		init ();
    		scanf ("%d", &m);
    		for (int i=1; i<=m; ++i)	{
    			scanf ("%s%s", &str1, &str2);
                if (id[str1] == id[str2])	continue;
    			add_edge (id[str1], id[str2], 1);
    			add_edge (id[str2], id[str1], 1);
    		    deg[id[str1]]++;    deg[id[str2]]++;
            }
    		int ans = 0;
            int idx = BFS (1);
            ans = d[BFS (idx)];
            for (int i=1; i<=n; ++i)    {
                if (d[i] == INF)    {
                    ans = -1;   break;
                }
            }
            printf ("%d
    ", ans);
    	}
    
    	return 0;
    }
    

      

    I The Power of Xiangqi

    比赛前走了一盘象棋,竟然就碰到了关于象棋的题目。

    状态压缩+枚举 J Scaring the Birds

    题意:有几个点可以放稻草人,问最少放几个稻草人才能保护所以稻草

    分析:没什么陷阱吧,主要看样例的R的范围有些纠结,放稻草人的点没有稻草的。只要状压枚举要放的数量和位置就可以了。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 55;
    const int INF = 0x3f3f3f3f;
    struct Point	{
        int x, y, r, step;
        Point () {}
        Point (int x, int y, int step) : x (x), y (y), step (step) {}
    }p[11];
    int n, m;
    int a[N][N];
    bool vis[N][N];
    int dx[] = {-1, 1, 0, 0};
    int dy[] = {0, 0, -1, 1};
    
    void map_init(void)	{
        for (int i=1; i<=n; ++i)	{
            for (int j=1; j<=n; ++j)	a[i][j] = 0;
        }
        for (int i=0; i<m; ++i)	{
            a[p[i].x][p[i].y] = 1;
        }
    }
    
    bool judge(void)	{
        for (int i=1; i<=n; ++i)	{
            for (int j=1; j<=n; ++j)	{
                if (!a[i][j])	return false;
            }
        }
        return true;
    }
    
    bool check(int x, int y)	{
        if (x < 1 || x > n || y < 1 || y > n || vis[x][y])	return false;
        else	return true;
    }
    
    void BFS(int u)	{
        int R = p[u].r;	vis[p[u].x][p[u].y] = true;
        queue<Point> Q;	Q.push (Point (p[u].x, p[u].y, 0));
        while (!Q.empty ())	{
            Point r = Q.front ();	Q.pop ();
            if (r.step > R)	continue;
            a[r.x][r.y] = 1;
            for (int i=0; i<4; ++i)	{
                int tx = r.x + dx[i], ty = r.y + dy[i];
                if (!check (tx, ty))	continue;
                vis[tx][ty] = true;
                Q.push (Point (tx, ty, r.step + 1));
            }
        }
    }
    
    int main(void)	{
        while (scanf ("%d", &n) == 1)	{
            if (!n)	break;
            scanf ("%d", &m);
            for (int i=0; i<m; ++i)	{
                scanf ("%d%d", &p[i].x, &p[i].y);
            }
            for (int i=0; i<m; ++i)	{
                scanf ("%d", &p[i].r);
            }
            int S = 1 << m;
            int ans = INF;
            for (int i=0; i<S; ++i)	{
                int num = __builtin_popcount (i);
                map_init ();
                for (int j=0; j<m; ++j)	{
                    if (i & (1 << j))	{
                        memset (vis, false, sizeof (vis));
                        BFS (j);
                    }
                }
                if (!judge ())	continue;
                ans = min (ans, num);
            }
            if (ans == INF)	puts ("-1");
            else	printf ("%d
    ", ans);
        }
    
        return 0;
    }
    

      

    最小生成树 K Outlets

    题意:求最小生成树,限制是p和q一定要有边连

    分析:作为第二水的题目我竟然不会写,kruskal的方法前先将p和q连上边就行了,或者Prim在p到q的距离变为0(优先出队列),然后再加回去。

    Kruskal

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 55;
    const int E = N * N;
    const double INF = 1e9;
    struct Edge	{
    	int u, v;
    	double w;
    	Edge () {}
        Edge (int u, int v, double w) : u (u), v (v), w (w) {}
        bool operator < (const Edge &r) const {
    		return w < r.w;
        }
    }edge[E];
    struct UF   {
        int rt[N], rk[N];
        void init(void) {
            memset (rt, -1, sizeof (rt));
            memset (rk, 0, sizeof (rk));
        }
        int Find(int x) {
            return rt[x] == -1 ? x : rt[x] = Find (rt[x]);
        }
        void Union(int x, int y)    {
            x = Find (x);   y = Find (y);
            if (x == y) return ;
            if (rk[x] > rk[y])  {
                rt[y] = x;  rk[x] += rk[y];
            }
            else    {
                rt[x] = y;  rk[y] += rk[x];
            }
        }
    }uf;
    int x[N], y[N];
    int tot;
    
    double sqr(int x, int y)	{
    	return (double) (x * x + y * y);
    }
    double cal_dis(int i, int j)	{
    	return sqrt (sqr (x[i] - x[j], y[i] - y[j]));
    }
    
    void add_edge(int u, int v, double w)	{
        edge[tot++] = Edge (u, v, w);
    }
    
    int p, q;
    
    int main(void)	{
    	int n;
    	while (scanf ("%d", &n) == 1)	{
    		if (!n)	break;
    		scanf ("%d%d", &p, &q);
    		if (p > q)  swap (p, q);
            for (int i=1; i<=n; ++i)	{
                scanf ("%d%d", &x[i], &y[i]);
    		}
    		uf.init (); tot = 0;
    		double ans = cal_dis (p, q);
            uf.rt[p] = q;
            for (int i=1; i<=n; ++i)	{
    			for (int j=i+1; j<=n; ++j)	{
    				add_edge (i, j, cal_dis (i, j));
                }
    		}
            sort (edge, edge+tot);
            for (int i=0; i<tot; ++i)   {
                int u = edge[i].u, v = edge[i].v;
                double w = edge[i].w;
                u = uf.Find (u);    v = uf.Find (v);
                if (u == v) continue;
                ans += w;
                uf.Union (u, v);
            }
    		printf ("%.2f
    ", ans);
    	}
    
    	return 0;
    }
    

    Prim

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 55;
    const int E = N * N;
    const double INF = 1e9;
    struct Edge	{
    	int v, nex;
    	double w;
    	Edge () {}
        Edge (int v, double w, int nex) : v (v), w (w), nex (nex) {}
        bool operator < (const Edge &r) const {
    		return w > r.w;
        }
    }edge[E];
    int head[N];
    bool vis[N];
    double d[N];
    int x[N], y[N];
    int p, q, e;
    
    void init(void)	{
    	memset (head, -1, sizeof (head));
    	e = 0;
    }
    
    void add_edge(int u, int v, double w)	{
        edge[e] = Edge (v, w, head[u]);
        head[u] = e++;
    }
    
    double sqr(int x, int y)	{
    	return (double) (x * x + y * y);
    }
    double cal_dis(int i, int j)	{
    	return sqrt (sqr (x[i] - x[j], y[i] - y[j]));
    }
    
    double Prim(int s)	{
    	memset (vis, false, sizeof (vis));
    	for (int i=0; i<55; ++i)	d[i] = INF;
    	priority_queue<Edge> Q;
    	double ret = 0;
        for (int i=head[s]; ~i; i=edge[i].nex)	{
    		int v = edge[i].v;	double w = edge[i].w;
    		if (d[v] > w)	{
    			d[v] = w;
                if (v == q) {
                    ret += d[v];
                    d[v] = 0;
                }
                Q.push (Edge (v, d[v], 0));
    		}
    	}
    	vis[s] = true;  d[s] = 0;
    	while (!Q.empty ())	{
    		int u = Q.top ().v;	Q.pop ();
    		if (vis[u])	continue;
    		vis[u] = true;	ret += d[u];
    		for (int i=head[u]; ~i; i=edge[i].nex)	{
    			int v = edge[i].v;	double w = edge[i].w;
    			if (v == q)	continue;
    			if (!vis[v] && d[v] > w)	{
    				d[v] = w;	Q.push (Edge (v, d[v], 0));
    			}
    		}
    	}
    	return ret;
    }
    
    int main(void)	{
    	int n;
    	while (scanf ("%d", &n) == 1)	{
    		if (!n)	break;
    		scanf ("%d%d", &p, &q);
    		if (p > q)  swap (p, q);
            for (int i=1; i<=n; ++i)	{
                scanf ("%d%d", &x[i], &y[i]);
    		}
    		init ();
    		for (int i=1; i<=n; ++i)	{
    			for (int j=i+1; j<=n; ++j)	{
    				add_edge (i, j, cal_dis (i, j));
    				add_edge (j, i, cal_dis (i, j));
    			}
    		}
    		printf ("%.2f
    ", Prim (p));
    	}
    
    	return 0;
    }
    

      

    编译人生,运行世界!
  • 相关阅读:
    codevs1028 花店橱窗布置
    bzoj1497 [NOI2006]最大获利 (最大闭合权图)
    扩展欧几里得算法总结
    codevs1033 蚯蚓的游戏问题
    bzoj1001狼抓兔子
    CSS3中的选择器
    LESS快速入门
    怎样让老浏览器兼容html5新标签
    http与https差异
    css3中的radius
  • 原文地址:https://www.cnblogs.com/Running-Time/p/4945837.html
Copyright © 2020-2023  润新知