看板娘加载较慢请耐心等待qwq~~~

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\n",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\n", 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('\n');
        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开始了卡常大战

posted @ 2019-10-11 13:51  椎名·六花  阅读(151)  评论(0编辑  收藏  举报