Typesetting math: 100%

和别人一起搞的模拟赛 (1) 题解

禁止以任何形式转载此文

solution

主讲人 IGL ALBL
试题 T2 T3 T1 T4 T5

现世,暗险

题意:给你一张有向图,距离为 22 的正整数次幂的两点间可以重新连一条长为 11 边,求在此情况下的从起点到终点最短路。

我们用 f[i][j][o]f[i][j][o] 表示点 ii 到点 jj 之间是否存在长为 2o2o 的边。有为真,否则为假。显然如果 f[i][k][o - 1] = f[k][j][o - 1] = 1f[i][k][o - 1] = f[k][j][o - 1] = 1,那 f[i][j][o] = 1f[i][j][o] = 1。预处理原图中相连的点为 f[i][j][0] = 1f[i][j][0] = 1。用 dis[i][j]dis[i][j] 表示两点间的最短距离,预处理原图中相连的点距离为 11,不相连的赋最大值。

然后倍增处理连新边,最后跑最短路即可。

数据这么小,FloydFloyd 可以了。

#include <bits/stdc++.h>

#define N 100

using namespace std;

template <typename T>
inline void read (T &a) {
	T x = 0, f = 1;
	char ch = getchar ();
	while (! isdigit (ch)) {
		(ch == '-') and (f = 0);
		ch = getchar ();
	}
	while (isdigit (ch)) {
		x = (x << 1) + (x << 3) + (ch ^ '0');
		ch = getchar ();
	}
	a = f ? x : -x;
}
template <typename T, typename ...A>
inline void read (T &t, A &...a) {
	read (t), read (a...);
}
template <typename T>
inline void print (T x) {
	if (x < 0) putchar ('-'), x = -x;
	if (x > 9) print (x / 10);
	putchar (x % 10 + '0');
}

int f[N][N][N];
int dis[N][N], n, m;

signed main () {
	freopen ("Marionette.in", "r", stdin);
	freopen ("Marionette.out", "w", stdout);
	read (n, m);
	memset (dis, 0x3f, sizeof dis);
	for (int i = 1, x, y; i <= m; i++) {
		read (x, y);
		f[x][y][0] = 1;
		dis[x][y] = 1;
	}
	for (int o = 1; o <= 64; o++) {
		for (int k = 1; k <= n; k++) {
			for (int i = 1; i <= n; i++) {
				for (int j = 1; j <= n; j++) {
					if (f[i][k][o - 1] and f[k][j][o - 1]) {
						f[i][j][o] = 1;
						dis[i][j] = 1;
					}
				}
			}
		}
	} // 处理可以直接跳的
	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				if (dis[i][j] > dis[i][k] + dis[k][j]) {
					dis[i][j] = dis[i][k] + dis[k][j];
				}
			}
		}
	} // 最短路
	print (dis[1][n]);
}

夜魇,如一

1.暴力

​ 考虑暴力枚举,对于每一种情况都追杀一次。

2.正解

​ 仔细观察,我们可以发现一些性质:我们设上帝的一次追杀事件为 (0, x)(0, x),我们可以得到一个小结论:

对于 xuixui,则这个事件插入在 ii 之前还是之后都是等价的,需要的话口胡证明。

​ 因此,我们可以尝试在每一个事件前插入 (0,ui)(0,ui) 之后直接暴力模拟,模拟一次的复杂度为 O(n)O(n),这题还有一个点—— nmnm,所以很容易想到当追杀无效直接跳过,则至多进行 3m-13m-1 次,复杂度O(nm)O(nm)

#include<bits/stdc++.h>
#define int long long
//#define rint register int
#define endl '\n'
using namespace std;
const int maxn = 6e4 + 5,mod = 1e9 + 7;

inline int read() {
    char c=getchar(),f=0;int t=0;
    for(;c<'0'||c>'9';c=getchar()) if(!(c^45)) f=1;
    for(;c>='0'&&c<='9';c=getchar()) t=(t<<1)+(t<<3)+(c^48);
    return f?-t:t;
}

inline void write(int num) {
	if(num < 0) {
		putchar('-');
		num = -num;
	}
	if(num > 9) write(num / 10);
	putchar(num % 10 + '0');
}

int n, m;
int hp[2005], tem[2005], ans[2005];
int lastin[2005];
struct thea{
	int u, v;
}Kill[maxn];

inline void as_one(int pos, int tar) {
	for(int i = 1; i <= m; ++ i) tem[i] = hp[i];
	
	tem[tar] --;
	
	for(int i = pos; i <= n; ++ i) if(tem[Kill[i].u] && tem[Kill[i].v]) tem[Kill[i].v] --;
	
	int t = 0;
	
	for(int i = 1; i <= m; ++ i) if(tem[i]) t ++;
	
	ans[t] += (pos - lastin[tar]); 
	
	lastin[tar] = pos;
}

signed main() {
	freopen("Seele.in","r",stdin);
	freopen("Seele.out","w",stdout);
	n = read(), m = read();
	
	for(int i = 1; i <= n; ++ i) Kill[i].u = read(), Kill[i].v = read();
	
	for(int i = 1; i <= m; ++ i) hp[i] = 3;
	
	for(int i = 1; i <= n; ++ i) {
		int x = Kill[i].u, y = Kill[i].v;
		if(hp[x] && hp[y]) {
			as_one(i, x);
			hp[y] --;
		}
	}
	
	int t = 0;
	
	for(int i = 1; i <= m; ++ i) if(hp[i]) t ++;
	for(int i = 1; i <= m; ++ i) {
		if(hp[i] == 1) ans[t - 1] += n + 1 - lastin[i];
		else ans[t] += n + 1 - lastin[i];
	}
	
	for(int i = 0; i <= m; ++ i) {
		printf("%lld ", ans[i]);
	}
	
	return 0;
	fclose(stdin);fclose(stdout);
}

薪炎,燎原

​ 这是一道恨不得拍到你脸上告诉你“这是四维偏序板子”的一道题,具体实现就是 CDQCDQCDQCDQ , 复杂度 O(nlog3n)O(nlog3n),用KDTKDT 的话会更快(但是我不会呵呵呵)

​ 然后因为我自己的原因我的 CDQCDQ 跑了2.5s2.5s(不会有暴力过了吧)

#include<bits/stdc++.h>
#define int long long
#define rint register int
#define endl '\n'
using namespace std;
const int maxn = 5e4 + 5,mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

inline int read() {
    char c=getchar(),f=0;int t=0;
    for(;c<'0'||c>'9';c=getchar()) if(!(c^45)) f=1;
    for(;c>='0'&&c<='9';c=getchar()) t=(t<<1)+(t<<3)+(c^48);
    return f?-t:t;
}

inline void write(int num) {
	if(num < 0) {
		putchar('-');
		num = -num;
	}
	if(num > 9) write(num / 10);
	putchar(num % 10 + '0');
}

int n, m, ans, M;
int b[maxn], poi_2D[maxn], poi_D[maxn];
struct node {
	int a, b, c, d, val, ans, id, choed;
	
	inline bool operator == (const node &x) const {
		return a == x.a && b == x.b && c == x.c && d == x.d;
	}
} poi[maxn], a[maxn];

inline int max(int a, int b) {
	if(a > b) return a; else return b;
}

inline bool cmp1(node a, node b) {
	if(a.a != b.a) return a.a < b.a;
	if(a.b != b.b) return a.b < b.b;
	if(a.c != b.c) return a.c < b.c;
	return a.d < b.d;
}

inline bool cmp2(node a, node b) {
	if(a.b != b.b) return a.b < b.b;
	if(a.c != b.c) return a.c < b.c;
	return a.d < b.d;
}

inline bool cmp3(node a, node b) {
	if(a.c != b.c) return a.c < b.c;
	return a.d < b.d;
}

struct AT {
	int tr[maxn];
	
	#define lowbit(x) (x & (-x))
	
	inline void add(int poi, int x) {
		for(int i = poi; i <= n; i += lowbit(i)) tr[i] = max(tr[i], x);
	}
	
	inline int ask(int poi) {
		int ans = -inf;
		for(int i = poi; i; i -= lowbit(i)) ans = max(tr[i], ans);
		return ans % mod;
	}
	
	inline void clear(int poi) {
		for(int i = poi; i <= n; i += lowbit(i)) {
			tr[i] = 0;
		}
	}
	
} TA;

void CDQ_3D(int l ,int r) {
//	cout << "FFGFFFF\n";
	if(l == r) return ;
	int mid = (l + r) >> 1;
	CDQ_3D(l, mid);
	
	rint i = l, j = mid + 1;
	
	sort(a + l, a + mid + 1, cmp3);
	sort(a + mid + 1, a + r + 1, cmp3);
	
	for(;j <= r; ++ j) {
		while(i <= mid && a[i].c <= a[j].c) {
			if(a[i].choed) TA.add(a[i].d, a[i].ans);
			i++ ;
		}
		if(!a[j].choed) a[j].ans = max(a[j].ans, (TA.ask(a[j].d) + a[j].val) % mod);
	}
	
	for(i = l; i < j; ++ i) if(a[i].choed) TA.clear(a[i].d);
	for(i = l; i <= r; ++ i) poi[poi_2D[a[i].id]] = a[i];
	for(i = l; i <= r; ++ i) a[i] = poi[i];
	CDQ_3D(mid + 1, r);
}

void CDQ_2D(int l, int r) {
//	cout << "SDFASFDASFDADASDASDASD\n";
	if(l == r) return ;
	int mid = (l + r) >> 1;
	CDQ_2D(l, mid);
	
	for(rint i = l; i <= mid; ++ i) a[i].choed = true;
	for(rint i = mid + 1; i <= r; ++ i) a[i].choed = false;
	
	sort(a + l, a + r + 1, cmp2);
	
	for(rint i = l; i <= r; ++ i) {
		poi_2D[a[i].id] = i;
	}
	
	CDQ_3D(l, r);
	
	for(rint i = l; i <= r; ++ i) {
		poi[poi_D[a[i].id]] = a[i];
	}
	
	for(rint i = l; i <= r; ++ i) a[i] = poi[i];
	
	CDQ_2D(mid + 1, r);
}

signed main() {
	freopen("Heavenlyfire.in","r",stdin);
	freopen("Heavenlyfire.out","w",stdout);
	n = read();
	M = read();
	for(rint i = 1; i <= n; ++ i) {
		a[i].a = read(), a[i].b = read(), a[i].c = read(), a[i].d = read() % mod, a[i].val = read() % mod;
		b[i] = a[i].d;
	}
	
	sort(b + 1, b + 1 + n);
	int tot = unique(b + 1, b + 1 + n) - b - 1;
	
	for(rint i = 1; i <= n; ++ i) {
		a[i].d = lower_bound(b + 1, b + 1 + tot, a[i].d) - b;
	}
	
	sort(a + 1, a + 1 + n, cmp1);
	
	for(rint i = 1; i <= n; ++ i) {
		if(a[i] == a[i - 1]) {
			a[m].val = (a[m].val + max(0LL, a[i].val)) % mod;
		}
		else a[++m] = a[i];
	}
	
	for(rint i = 1; i <= n; ++ i) a[i].ans = a[i].val, a[i].id = i, poi_D[a[i].id] = i;
		
	CDQ_2D(1, n);
	
	ans = -inf;
	
	for(rint i = 1; i <= n; ++ i) ans = max(ans, a[i].ans);

	printf("%lld\n", ans % mod);
	if(ans % mod < M) cout << "The Honkai befalled";
	else cout << "May all the beauty be blessed";
	fclose(stdin);fclose(stdout);
}

镜缘

blog

一切开始之前,我们先推导一个引理:

f(n)=ni=0i2,n0f(n)=n(n+1)(2n+1)6f(n)=ni=0i2,n0f(n)=n(n+1)(2n+1)6

这里提供五种推导它的方法。

一、查找法

通过搜索引擎,题解,文献等,我们找到了这个公式:

f(n)=n(n+1)(2n+1)6f(n)=n(n+1)(2n+1)6

[doge]

二、归纳法

f(n)=n(n+1)(2n+1)6 =n(n+1)(n+12)3f(0)=0f(n)=f(n1)+n23f(n)=(n1)n(n12)+3n2=(n332n+12n)+3n2=n3+32n2+12n=n(n+12)(n+1)故对于所有的n0,f(n)=n(n+1)(2n+1)6f(n)=n(n+1)(2n+1)6 =n(n+1)(n+12)3f(0)=0f(n)=f(n1)+n23f(n)=(n1)n(n12)+3n2=(n332n+12n)+3n2=n3+32n2+12n=n(n+12)(n+1)n0,f(n)=n(n+1)(2n+1)6

三、二重合式展开与收缩

f(n)=nk=1k2=nj=1nk=jk=nj=1j+n2(nj+1)=12nj=1(n(n+1)+jj2)=12n2(n+1)+14n(n+1)12f(n)=12n(n+12)(n+1)12f(n)32f(n)=12n(n+12)(n+1)f(n)=n(n+1)(2n+1)6f(n)=nk=1k2=nj=1nk=jk=nj=1j+n2(nj+1)=12nj=1(n(n+1)+jj2)=12n2(n+1)+14n(n+1)12f(n)=12n(n+12)(n+1)12f(n)32f(n)=12n(n+12)(n+1)f(n)=n(n+1)(2n+1)6

nk=1k2=nj=1nk=jknk=1k2=nj=1nk=jk 的解释:

我们知道,一个数 nn 加和 nn 次,就是 n2n2

将右式展开:

1+2+3+4+5+6+...+n
  2+3+4+5+6+...+n
    3+4+5+6+...+n
      4+5+6+...+n
        5+6+...+n
          6+...+n
            ...
            ...+n

刚好每个数 kkkk 次,与原式等价。

四、瞎搞法

利用恒等式 (n+1)3=n3+3n2+3n+1(n+1)3=n3+3n2+3n+1,可以得到:

(n+1)3n3=3n2+3n+1n3(n1)3=3(n1)2+3(n1)+12313=3(12)+31+1(n+1)3n3=3n2+3n+1n3(n1)3=3(n1)2+3(n1)+12313=3(12)+31+1

把这 nn 个等式两端分别相加,得:

(n+1)31=3×n(n+1)2+n+3ni=1i2n3+3n2+3n=3×n(n+1)2+n+3ni=1i2(n+1)31=3×n(n+1)2+n+3ni=1i2n3+3n2+3n=3×n(n+1)2+n+3ni=1i2

整理后得:

ni=1i2=n(n+1)(2n+1)6ni=1i2=n(n+1)(2n+1)6

五、扰动法

这是最实用的方法。

对于 f(n+1)f(n+1) ,抽出 (n+1)2,02(n+1)2,02

f(n)+(n+1)2=nk=0(k+1)2=nk=0(k2+2k+1)=nk=0k2+2nk=0k+nk=01=f(n)+2nk=0k+(n+1)nk=0k=12((n+1)2(n+1))=n(n+1)2f(n)+(n+1)2=nk=0(k+1)2=nk=0(k2+2k+1)=nk=0k2+2nk=0k+nk=01=f(n)+2nk=0k+(n+1)nk=0k=12((n+1)2(n+1))=n(n+1)2

额。。。有个毛用?

等等,二次幂可以推出一次幂求和,那是不是三次幂可以推出二次幂求和?

g(n)=nk=0k3g(n)=nk=0k3

g(n)g(n) 中抽出 (n+1)3,03(n+1)3,03

g(n)+(n+1)3=nk=0(k+1)3=nk=0(k3+3k2+3k+1)=nk=0k3+3nk=0k2+3nk=0k+nk=01=g(n)+3f(n)+3n(n+1)2+(n+1)3f(n)=(n+1)33n(n+1)2(n+1)=n3+3n2+3n+13n2+3n2n1=n3+3n22+n2=(n+1)(n+12)nf(n)=n(n+1)(n+12)3g(n)+(n+1)3=nk=0(k+1)3=nk=0(k3+3k2+3k+1)=nk=0k3+3nk=0k2+3nk=0k+nk=01=g(n)+3f(n)+3n(n+1)2+(n+1)3f(n)=(n+1)33n(n+1)2(n+1)=n3+3n2+3n+13n2+3n2n1=n3+3n22+n2=(n+1)(n+12)nf(n)=n(n+1)(n+12)3

通过此方法,我们可以从 n+1n+1 次幂推出 nn 次幂!!!

好,接下来回归题目:

11RR 枚举 xx,那 yy 的最大值为 Rx2Rx2,因为再大会超出这个圆。

当然 yy 向下取整。

xx 的贡献总和为 x2yx2yyy 的贡献总和为 y(y+1)(2y+1)6y(y+1)(2y+1)6

相加即可。

#include <bits/stdc++.h>

#define int unsigned long long
#define le double
#define mod 1000000007

using namespace std;

inline int qpow (int a, int b) {
	int res = 1;
	while (b) {
		(b & 1) and (res = res * a % mod);
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

int r, ans, k, inv;

signed main () {
	freopen ("Mirror.in", "r", stdin);
	freopen ("Mirror.out", "w", stdout);
	inv = qpow (6, mod - 2);
	cin >> r;
	for (int x = 0, y; x * x <= r; x++) {
		y = sqrt ((le) r - x * x);
		k = (y * (y + 1) % mod * (y * 2 % mod + 1) % mod) * inv % mod + (x * x % mod * y % mod) % mod;
		ans = (ans + k) % mod;
	}
	cout << ans * 4 % mod << endl;
//	cout << "The run time is: " <<(double)clock() / CLOCKS_PER_SEC << "s" << endl;
}

廊中燥

话说这题是我自己口胡的,所以很水。

先给结论:

kS(m,n)φ(k)=m×nkS(m,n)φ(k)=m×n

首先思考判定条件 n mod k + m mod kkn mod k + m mod kk 的等价判定式。

modmod 有关的,除法?除法与向下取整结合,会有:

(n+m)knkmk(n+m)knkmk

这个式子与原判定式等价。

那么再看这个合式,既然有了新的判定式,那么这个式子也该找个等价式。

发现 S(m,n)S(m,n) 其实就是在 11m+nm+n 个数里筛掉一些数,

也就是:

m+nk=1φ(k)m+nknk=1φ(k)ndmk=1φ(k)mdm+nk=1φ(k)m+nknk=1φ(k)ndmk=1φ(k)md

接下来考虑如何计算这个冗长的式子。

暴力枚举肯定不行。

发现这个长式子可以分成三截,算其中一截就行。

我们先从中拉出来一截转换一下:

nk=ni=1[di]nk=ni=1[di]

带入其中那部分式子

nk=1φ(k)nd=nk=1φ(k)nj=1[kj]=nj=1jk=1[kj]φ(k)=nj=1kjφ(k)=nj=1j=n(n+1)2nk=1φ(k)nd=nk=1φ(k)nj=1[kj]=nj=1jk=1[kj]φ(k)=nj=1kjφ(k)=nj=1j=n(n+1)2

答案显而易见了,

(m+n)(m+n+1)2n(n+1)2m(m+1)2=m×n(m+n)(m+n+1)2n(n+1)2m(m+1)2=m×n

#include <bits/stdc++.h>

#define int long long
#define mod 1000000007

using namespace std;

int n, m;

signed main () {
	freopen ("Wzybqdp.in", "r", stdin);
	freopen ("Wzybqdp.out", "w", stdout);
	cin >> n >> m; cout << ((n % mod) * (m % mod)) % mod;
}
posted @   aleph_blanc  阅读(48)  评论(0编辑  收藏  举报
编辑推荐:
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
阅读排行:
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
· Ai满嘴顺口溜,想考研?浪费我几个小时
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
点击右上角即可分享
微信分享提示