zoukankan      html  css  js  c++  java
  • 2019.10.10模拟赛

    T1 马里奥

    (n * m)的地图,可以由‘#’到达上方的‘#’,代价是高度差,也可以横向到达相邻的‘#’。求最小的代价(不是代价和,是最小的一个代价)。
    解1:建图,二分最小的代价,跑最短路。时间复杂度(O(n^2log^2(n)))

    inline int calc(const int &x, const int &y) { return (x - 1) * m + y; }
    int st,ed;
    int d[MAXN * MAXN];
    bool v[MAXN * MAXN];
    inline void spfa(const int mid)
    {
    	memset(d, 0x3f, sizeof(d));
    	memset(v, 0, sizeof(v));
    	queue<int> q;
    	q.push(st);
    	d[st] = 0;
    	v[st] = 1;
    	while(q.size())
    	{
    		register int x = q.front();
    		q.pop();
    		v[x] = 0;
    		for(register int i = head[x]; i; i = e[i].nxt)
    		{
    			register int y = e[i].ver, z = e[i].edge;
    			if(z > mid)
    				continue;
    			if(d[y] > d[x] + z)
    			{
    				d[y] = d[x] + z;
    				if(!v[y])
    					q.push(y),v[y] = true;	
    			}
    		}
    	}
    }
    inline int find()
    {
    	register int l = 0,r = n, mid, res = 0;
    	while(l <= r)
    	{
    		mid = (l + r) >> 1;
    		spfa(mid);
    		if(d[ed] == 0x3f3f3f3f)
    			l = mid + 1;
    		else
    			res = mid,r = mid - 1;
    	}
    	return res;
    }
    int main()
    {
    	scanf("%d %d",&n, &m);
    	for(register int i = 1; i <= n; ++i)	
    		scanf("%s",s[i] + 1);
    	scanf("%d %d",&x, &y);
    	if(x == n)
    	{
    		puts("0");
    		fclose(stdin);
    		fclose(stdout);
    		return 0;
    	}
    	st = calc(n,1);
    	ed = calc(x,y);
    	for(register int i = n; i; --i)
    	{
    		for(register int j = 1; j < m; ++j)
    		{
    			if(s[i][j] == '#' && s[i][j+1] == '#')
    			{
    				register int x = calc(i,j);
    				add(x, x+1, 0);
    				add(x+1, x, 0);
    			}
    		}
    		for(register int j = 1; j <= m; ++j)
    		{
    			if(s[i][j] == '#')
    				for(register int k = i - 1; k;--k)
    				{
    					if(s[k][j] == '#')
    					{
    						register int x = calc(i, j);
    						register int y = calc(k, j);
    						register int z = i - k;
    						add(x, y, z);
    						add(y, x, z);
    						break;
    					}
    				}	
    		}
    	}
    	register int ans = find();
    	printf("%d
    ",ans);
    	return 0;
    }
    

    解2:使用并查集维连通性,贪心的取全局最小边。类似(kruskal)最小生成树算法。时间复杂度(O(n^2 log^2(n)))
    实际因为这个算法常数小,比二分+最短路快不少。

    inline int calc(const int &x, const int &y) { return (x - 1) * m + y; }
    struct node
    {
    	int x, y, edge;
    	inline bool operator<(const node &t) const { return edge < t.edge; }
    } e[MAXN * MAXN << 1];
    int tot;
    int st, ed;
    int fa[MAXN * MAXN];
    inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    int main()
    {
    	scanf("%d %d", &n, &m);
    	for (register int i = 1; i <= n; ++i)
    		scanf("%s", s[i] + 1);
    	scanf("%d %d", &x, &y);
    	if (x == n)
    	{
    		puts("0");
    		fclose(stdin);
    		fclose(stdout);
    		return 0;
    	}
    	st = calc(n, 1);
    	ed = calc(x, y);
    	for (register int i = n; i; --i)
    	{
    		for (register int j = 1; j < m; ++j)
    		{
    			if (s[i][j] == '#' && s[i][j + 1] == '#')
    			{
    				register int x = calc(i, j);
    				e[++tot].x = x;
    				e[tot].y = x + 1;
    				e[tot].edge = 0;
    			}
    		}
    		for (register int j = 1; j <= m; ++j)
    		{
    			if (s[i][j] == '#')
    				for (register int k = i - 1; k; --k)
    				{
    					if (s[k][j] == '#')
    					{
    						register int x = calc(i, j);
    						register int y = calc(k, j);
    						register int z = i - k;
    						e[++tot].x = x;
    						e[tot].y = y;
    						e[tot].edge = z;
    						break;
    					}
    				}
    		}
    	}
    	for (register int i = 1; i <= n * n; ++i)
    		fa[i] = i;
    	sort(e + 1, e + tot + 1);
    	register int ans = n * 2;
    	for (register int i = 1; i <= tot; ++i)
    	{
    		int x = e[i].x, y = e[i].y, z = e[i].edge;
    		x = find(x);
    		y = find(y);
    		if (x == y)
    			continue;
    		fa[x] = y;
    		if (find(st) == find(ed))
    		{
    			ans = z;
    			break;
    		}
    	}
    	printf("%d
    ", ans);
    }
    

    T2 祭司

    (i)个变量,每个变量有取值范围,将这些变量分成两组,使两组变量之和的差的最大值最小,求这个最小值。
    正解:我们设分组为(A,B),(sum1)为该组最小值之和,(sum2)为该组最大值之和,(sum)为该组所有值之和。
    易得最大值 = (max(sum2(A) - sum1(B),sum1(A) - sum2(B)) = max(sum(A) - sum1(A + B),sum(A) - sum2(A+B) ))
    (sum1(A + B),sum2(A+B))都是常量,答案只与(sum(A))有关。
    使用可行性DP,求出所有可能的(sum(A)),统计最优解即可。
    可以使用bitset优化。

    int main()
    {
        poread(n);
        for (register short i = 1; i <= n; ++i)
            poread(lim[i].l), poread(lim[i].r);
        for (register short i = 1; i <= n; ++i)
            sum1 += lim[i].l, sum2 += lim[i].r;
        f[0] = true;
        for (register short i = 1; i <= n; ++i)
            f |= (f << (lim[i].l + lim[i].r));
        register unsigned short i = (sum2 + sum1) >> 1;
        for (; i >= 0; --i)
            if (f[i])
                break;
        printf("%d", sum2 - i);
    }
    

    解2:模拟退火(数据太水甚至不用调参)

    double eps = 1e-5, delta = 0.993;
    struct node
    {
        short l, r;
    } lim[MAXN];
    bool tag[MAXN];
    int n, m;
    inline unsigned short calc()
    {
        unsigned short res1 = 0, res2 = 0;
        unsigned short sum1 = 0, sum2 = 0;
        for (register short i = 1; i <= n; ++i)
            (tag[i]) ? (sum1 += lim[i].l) : (sum2 += lim[i].r);
        res1 = abs(sum2 - sum1);
        sum2 = sum1 = 0;
        for (register short i = 1; i <= n; ++i)
            (tag[i]) ? (sum1 += lim[i].r) : (sum2 += lim[i].l);
        res2 = abs(sum2 - sum1);
        return max(res1, res2);
    }
    int ans = INT_MAX;
    int ans_fire;
    inline void sa()
    {
        ans_fire = INT_MAX;
        double T = 1000;
        for (register int i = 1; i <= n; ++i)
            tag[i] = rand() & 1;
        while (T > eps)
        {
            T *= delta;
            register int x = rand() % n + 1, y = rand() % n + 1;
            if(x == y)
                continue;
            swap(tag[x], tag[y]);
            int now = calc();
            if(now < ans_fire || rand() % 1000 < T)
                ans_fire = now;
            else
                swap(tag[x], tag[y]);
        }
    }
    int main()
    {
        srand(time(0));
        poread(n);
        for (register int i = 1; i <= n; ++i)
            poread(lim[i].l), poread(lim[i].r);
        for (register int i = 1; i <= 200; ++i)
        {
            sa();
            ans = min(ans_fire, ans);
        }
        cout << ans << endl;
    }
    

    解3:直接(rand())在哪个分组,真的能过√。
    理论上来说(random\_shuffle)能过的但是前几个点不太行。

    for (register short i = 1; i <= 5000; ++i)
    {
        for (register short j = 1; j <= n; ++j)
            whe[j] = mt() & 1;
        ans = min(ans, calc());
    }
    

    T3 AK(真就AK啊

    给你一个数字序列,每次查询一段区间的数字和,并且把它们都变成原来的平方。答案对(2305843008676823040)取模。
    大佬们通过打表发现某个数平方几次之后就不动了。。。
    所以可以直接线段树维护这段区间。
    官方证明:
    官方题解
    数据不强,貌似30次之后就是定值了。
    由于模数很大会爆long long,需要龟速乘。
    __int128表示不服

    inline long long spow(long long a, long long b)
    {
        register long long ans = 0;
        for (; b; b >>= 1)
        {
            if (b & 1)
            {
                ans = ans + a;
                if (ans > MOD)
                    ans -= MOD;
            }
            a = (a << 1);
            if (a > MOD)
                a -= MOD;
        }
        return ans;
    }
    const int MAXN = 65537;
    class T
    {
    #define ls (tr << 1)
    #define rs (tr << 1 | 1)
    public:
        struct node
        {
            long long sum;
            bool tag;
            node() {}
            node(const long long &_sum, const unsigned char &_tag)
            {
                sum = _sum;
                tag = _tag;
            }
            friend inline node operator+(const node &a, const node &b)
            {
                long long sum = a.sum + b.sum;
                if (sum > MOD)
                    sum -= MOD;
                return node(sum, a.tag & b.tag);
            }
        } tree[MAXN << 2];
        inline void build(int tr, int L, int R)
        {
            if (L == R)
            {
                poread(tree[tr].sum);
                return;
            }
            register int mid = (L + R) >> 1;
            build(ls, L, mid);
            build(rs, mid + 1, R);
            // tree[tr] = tree[ls] + tree[rs];
            tree[tr].sum = tree[ls].sum + tree[rs].sum;
            if (tree[tr].sum > MOD)
                tree[tr].sum -= MOD;
            tree[tr].tag = tree[ls].tag & tree[rs].tag;
        }
        inline void change(int tr, int L, int R, int l, int r)
        {
            if (tree[tr].tag)
                return;
            if (L == R)
            {
                register long long tmp = tree[tr].sum;
                tree[tr].sum = spow(tree[tr].sum, tree[tr].sum);
                if (tmp == tree[tr].sum)
                    tree[tr].tag = 1;
                return;
            }
            register int mid = (L + R) >> 1;
            if (r <= mid)
                change(ls, L, mid, l, r);
            else if (l > mid)
                change(rs, mid + 1, R, l, r);
            else
                change(ls, L, mid, l, mid), change(rs, mid + 1, R, mid + 1, r);
            // tree[tr] = tree[ls] + tree[rs];
            tree[tr].sum = tree[ls].sum + tree[rs].sum;
            if (tree[tr].sum > MOD)
                tree[tr].sum -= MOD;
            tree[tr].tag = tree[ls].tag & tree[rs].tag;
        }
        inline long long query(int tr, int L, int R, int l, int r)
        {
            if (L == l && R == r)
                return tree[tr].sum;
            register int mid = (L + R) >> 1;
            if (r <= mid)
                return query(ls, L, mid, l, r);
            else if (l > mid)
                return query(rs, mid + 1, R, l, r);
            else
            {
                long long res = query(ls, L, mid, l, mid) + query(rs, mid + 1, R, mid + 1, r);
                if (res > MOD)
                    res -= MOD;
                return res;
            }
        }
    #undef ls
    #undef rs
    } t;
    int n, m;
    int main()
    {
        poread(n);
        poread(m);
        t.build(1, 1, n);
        for (register int i = 1, l, r; i <= m; ++i)
        {
            poread(l), poread(r);
            fdata::out(t.query(1, 1, n, l, r));
            putchar('
    ');
            t.change(1, 1, n, l, r);
        }
    }
    

    如果用__int128可以跑得很快(毕竟少常数)

    const __int128 YU = 1ll;
    inline void change(int tr, int L, int R, int l, int r) {
            if (tree[tr].tag)
                return;
            if (L == R) {
                register unsigned long long tmp = tree[tr].sum;
                tree[tr].sum = YU * tree[tr].sum * tree[tr].sum % MOD;
                if (tmp == tree[tr].sum)
                    tree[tr].tag = 1;
                return;
            }
            register int mid = (L + R) >> 1;
            if (r <= mid)
                change(ls, L, mid, l, r);
            else if (l > mid)
                change(rs, mid + 1, R, l, r);
            else
                change(ls, L, mid, l, mid), change(rs, mid + 1, R, mid + 1, r);
            // tree[tr] = tree[ls] + tree[rs];
            tree[tr].sum = tree[ls].sum + tree[rs].sum;
            if (tree[tr].sum > MOD)
                tree[tr].sum -= MOD;
            tree[tr].tag = tree[ls].tag & tree[rs].tag;
        }
    

    ps:在HOJ开始了卡常大战

  • 相关阅读:
    转:选择学习“下一个”程序语言
    再谈 Web 字体的现状与未来
    堪称2008年最漂亮的50组图标(上)
    堪称2008年最漂亮的50组图标(下)
    回帖整理: 互联网的未来, 我们的未来, 算一个预告吧, 有空我会把这些观点一一展开
    [回帖整理]创业建议
    也论PageController/FrontController与MVC
    [回帖整理] 创业难
    是否非要用interface关键字来实现接口?
    又论社区风气, 与程序员是干嘛地的.
  • 原文地址:https://www.cnblogs.com/Shiina-Rikka/p/11653741.html
Copyright © 2011-2022 走看看