大假期集训模拟赛14

置换

题目描述

\(negii\) 沉迷于研究计算机的构造,他发现简单的二进制竟然能完成如此复杂的计算,当他知道二进制的所有位运算的操作之后,他突发奇想,他想找一种方法能实现二进制位的翻转。具体地,就是我们要将 \(7(0111)\) 翻转成 \(14(1110)\)\(11(1011)\) 翻转成 \(13(1101)\)

现在我们给定二进制位数 \(k\) 以及 \(n\) 个满足二进制位数不超过 \(k\) 的数,我们需要输出对应翻转后的数的十进制表示

由于读入量较大,所以 \(n\) 个数由本机随机生成,具体生成方式如下

int my_rand()
{
    Seed=(214013LL*Seed+2531011)&((1<<k)-1);
    return Seed;
}

我们会给出初始的 \(Seed\),我们会调用 \(n\) 次函数,得到需要翻转的 \(n\) 个数

我们还会采取特殊的输出方式,我们将所有答案 \(hash\) ,并输出最后的值即可

int ans=0;
void my_hash(int x)
{
    ans=((long long)ans*233+x)%99824353;
}

我们每得到一个数,进行翻转后,我们就会调用一次这个函数,其中传入参数 \(x\) 为翻转后的数,我们最后输出 \(ans\) 的值即可

输入格式

\(3\) 个数,分为 \(n\)\(k\)\(Seed\)

输出格式

一行,一个整数,表示最后 \(hash\) 值。

样例

样例输入

5 3 233

样例输出

76005766

数据范围与提示

对于前 \(50\%\) 的数据, \(k\leq 17\)

对于前 \(70\%\) 的数据, \(k\leq 20\)

对于前 \(90\%\) 的数据, \(k\leq 23\)

对于 \(100\%\) 的数据, \(n\leq 2^k ,k\leq 25\)

思路

首先先理解一下题意吧,给你三个数,让你用上面的 \(rand\) 函数来造数据,其实它一点都不 \(rand\) ,多试几次就会发现,总会是那几个数,顺序都不带变的。

最后输出的时候,将每个翻转后的值 \(hash\) 一遍,输出 \(ans\) 即可。

这样我们就主要思考怎么翻转一个二进制的数了。


  • 首先明确一个要点:\(k\) 表示的是一个数 \(x\) 转化成二进制的位数。

比如 \(k=3\) 时,\(1\) 转化为二进制为 \(001\) ,翻转后为 \(100\) ,即 \(4\)

\(k=4\) 时,\(1\) 转化为二进制为 \(0001\) ,翻转后为 \(1000\) ,即 \(8\)

这样我们就更加明确了题意。


怎么实现一个数的翻转呢?

比如我们要翻转 \(0001011\)

我们将它右移一位,变为 \(000101\)

如果我们知道了 \(000101\) 翻转后的数字,将它加上 \(2^{k-1}\),不就是我们要求的值了吗。

所以我们就可以用前面的将后面的值推出来。

for (int i = 1; i <= EJZ[25]; i++) {
    if (i % 2 == 0) a[i] = a[i >> 1] >> 1;//如果最后一位为0,就不需要加上2^k-1
    else a[i] = (a[i >> 1] >> 1) + EJZ[k - 1];//最后一位为1,需要加上2^k-1
}

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn = 4e7 + 50, mod = 99824353;
inline int read(){
	int x = 0, w = 1;
	char ch;
	for (; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

int EJZ[30]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304 ,8388608 , 16777216, 33554432, 67108864};

int n, k, Seed;
int a[maxn];

int my_rand(){
    Seed = (214013LL * Seed + 2531011) & ((1 << k) - 1);
    return Seed;
}

ll ans = 0;

void my_hash(int x){
    ans = (ans * 233 + x) % mod;
}

int main(){
	n = read(), k = read(), Seed = read();
	for (int i = 1; i <= EJZ[25]; i++) {
		if (i % 2 == 0) a[i] = a[i >> 1] >> 1;//如果最后一位为0,就不需要加上2^k-1
		else a[i] = (a[i >> 1] >> 1) + EJZ[k - 1];//最后一位为1,需要加上2^k-1
	}
	for (int i = 1; i <= n; i++) {	
		int x = my_rand();
		my_hash(a[x]);
	}
	printf("%lld\n", ans);
	return 0;
}

字符串

题目描述

\(negii\)\(starria\) 是好朋友。他们在一起做字符串游戏。

我们定义对于一个字符串的翻转操作:比如翻转的串为 \(R\),那么他会将前 \(|R|−1\) 个字符倒序后,插入到该串的最后。举个例子,串 \(abd\) 进行翻转操作后,将得到 \(abdba\)

\(negii\) 进行了若干次(可能为 \(0\) 次)字符串的翻转操作。

\(negii\)\(starria\) 展示出了一个非空串 \(S\)\(S\) 是一个串 \(R\) 的前缀。他想考考 \(starria\),初始的串 \(R\) 的长度可能是多少。

\(starria\) 找到了正在参加模拟赛的你,请你来帮她解决这个问题。但聪明的 \(starria\) 发现,所有超过 \(|S|\) 的整数都一定是 \(R\) 的可能长度,因此你只需要告诉她不超过的 \(|S|\)\(R\) 的可能长度即可。

输入格式

输入包含多组数据,第一行一个整数 \(T\) 表示数据组数。

接下来依次描述每组数据,对于每组数据,一行一个仅由小写字母组成的非空字符串 \(S\)

输出格式

对于每组数据,输出 \(1\) 行,从小到大输出 \(|R|\) 的所有不超过 \(|S|\) 的可能值,所有值之间用单个空格隔开。

样例

样例输入

3
abcdcb
qwqwq
qaqaqqq

样例输出

4 6
2 3 4 5
6 7

数据范围与提示

对于 \(40\%\) 的数据,保证 \(\sum |S|\leq 5\times 10^2\)

对于 \(60\%\) 的数据,保证 \(\sum |S|\leq 5\times 10^3\)

对于 \(100\%\) 的数据,保证 \(|S|\leq 10^6,\sum |S|\leq 5\times 10^6\)

思路

我们会发现:

  • 当我们枚举后 \(n/2\) 的点时,我们向两边逐渐扩展:

\(a[l]=a[r]\) ,继续扩展,若 \(r=n\) 了,则证明这个点符合条件。

\(a[l]!=a[r]\) ,停止扩展,这个点也不会被记录到答案中。

  • 当我们枚举前 \(n/2\) 的点时,我们也向两边扩展,不过跟上面不同的是,\(l\) 会先到达边界 \(1\)

\(a[l]=a[r]\) ,继续扩展,若 \(l=1\) 了,并不能证明这个点是答案,需要判断当前走到的 \(r\) 点是否符合条件,若 \(r\) 点符合条件,这个点就是答案。

\(a[l]!=a[r]\) ,停止扩展,这个点也不会被记录到答案中。


  • 如果我们用 \(n^2\) 直接去跑,肯定会 \(TLE\) ,所以我们需要借助一些效率比较高的算法。

法一

可以用一波 \(hash\),维护一个正向的 \(hash\) 值,维护一个反向的 \(hash\) 值,比对正反 \(hash\) 值即可。

法二

可以用一波 \(manacher\),记录下以每个节点为中心的回文长度,然后进行比较即可。

代码

法一

#include <bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int maxn = 1e6 + 5;
int T, n;
char a[maxn];
ull h[maxn], f[maxn], p[maxn];
ull H(int l, int r) {
	return h[r] - h[l-1] * p[r-l+1];
}
ull F(int l, int r) {
	return f[l] - f[r+1] * p[r-l+1];
}
int main() {
	p[0] = 1;
	for (int i = 1; i <= 1e6; ++i)
		p[i] = p[i-1] * 131;
	scanf("%d", &T);
	while (T--) {
		scanf("%s", a + 1);
		n = strlen(a + 1);
		f[n+1] = 0;
		for (int i = 1; i <= n; ++i)//正向hash
			h[i] = h[i-1] * 131 + a[i];
		for (int i = n; i >= 1; --i)//反向hash
			f[i] = f[i+1] * 131 + a[i];
		for (int i = 2; i < n; ++i) {
			int l = min(i, n - i + 1);
			if (H(i - l + 1, i) != F(i, i + l - 1)) continue;
			l = i-1 << 1;
			if (l > n || H(1, n - l) == H(1 + l, n)) printf("%d ", i);
		}
		printf("%d\n", n);
	}
	return 0;
}

法二

#include <bits/stdc++.h>
using namespace std;

const int maxn = 5e6 + 50, INF = 0x3f3f3f3f, mod = 1e9 + 7;
inline int read(){
	int x = 0, w = 1;
	char ch;
	for (; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

int T, n, tot, m;
char a[maxn];
char str[maxn];
bool flag[maxn];
int ans[maxn];
bool isblack[maxn];

void Init(){
	str[1] = '$';
	str[2] = '#';
	for (int i = 1; i <= n; i++) {
		str[i * 2 + 1] = a[i];
		str[i * 2 + 2] = '#';
	}
	m = n * 2 + 2;
}

void manacher(){//经典manacher
	int c = 0 ,r = 0;
	for (int i = 1; i <= m; i++) {
		if (r > i) {
			ans[i] = min (ans[2 * c - i], r - i);
		}else {
			ans[i] = 1;
		}
		for (;str[i + ans[i]] == str[i - ans[i]]; ans[i]++);
		if (ans[i] + i > r){
			r = ans[i] + i;
			c = i;
		}
	}
}

int l, r;

bool Judge(int k){
	if(flag[k]) return isblack[k];
	flag[k] = 1;
	r = n - k + 1;
	l = k;
	if (l >= r) {//n/2以后的点
		if (ans[2 * k + 1] / 2 >= r) {
			isblack[k] = 1;
			return 1;
		}else {
			isblack[k] = 0;
			return 0;
		}
	}else {
		if (ans[2 * k + 1] / 2 < l) {//n/2之前的点
			isblack[k] = 0;
			return 0;
		}
		isblack[k] = Judge(k + ans[2 * k + 1] / 2 - 1);
		return isblack[k];
	}
}

int main(){
	T = read();
	while (T--) {
		tot = 0;
		memset(isblack, 0, sizeof isblack);
		memset(ans, 0, sizeof ans);
		memset(flag, 0, sizeof flag);
		scanf("%s", a + 1);
		n = strlen(a + 1);
		if(n==1){
			printf("1\n");
			continue;
		}
		Init();
		manacher();
		for (int i = 2; i <= n; i++) {
			if (!flag[i]) Judge(i);
		}
		for (int i = 1; i <= n; i++) {
			if (isblack[i]) {
				printf("%d ",i);
			}
		}	
		printf("\n");
	}
	return 0;
}

饼干

题目描述

佩琪最喜欢吃饼干了!但是现在他不准备自己吃,她决定和贝茜分享自己的饼干。佩琪有四种口味的饼干,分别是巧克力味、香草味、红豆味和椰子味。可惜的是,佩琪已经把椰子味的饼干全吃光了,所以现在只剩下前三种了233。。。

巧克力味饼干能带来 \(A\) 的美味度,香草味饼干能带来 \(B\) 的美味度,而红豆味饼干可以带来 \(A+B\) 的美味度。

佩琪会拿出不多于 \(N\) 块饼干作为送给贝茜的礼物。

为了显得真诚,她想让她拿出的饼干的美味度之和恰好为 \(K\)

为了显得体面,她决定用一个精美的盒子来装这些饼干。盒子内部有 \(N\) 个位置,每个位置都可以放一块饼干,或者不放。

现在佩琪想知道有多少种方案可以既显得真诚又显得体面。两种方案不同,当且仅当盒子中某个位置放置的饼干味道不同,或者一个放了饼干而另一个没有。

佩琪自己当然不会做啦,所以她决定请你来帮她解决这个问题。为了防止答案过大,你只需要告诉她答案对 \(998244353\) 取模的结果就行了。

输入格式

输入只有一行,包含四个整数,分别为\(N\)\(A\)\(B\)\(K\)

输出格式

输出一行一个整数,即答案对 \(998244353\) 取模的结果。

样例

样例输入

4 1 2 5

样例输出

40

数据范围与提示

对于前 \(30\%\) 的数据,\(1\leq N\leq 10,0\leq K\leq 500\)

对于前 \(50\%\) 的数据,\(1\leq N\leq 1000,0\leq K\leq 3000\)

对于前 \(80\%\) 的数据,\(1\leq N\leq 1e5,0\leq K\leq 2e10\)

对于 \(100\%\) 的数据,\(1\leq N\leq 1e7,0\leq K\leq 1e15,0\leq A,B\leq 1e5\)

思路

  • \(30opts\) 的分段

显然数据很小,可以直接走 \(DFS\) ,要么不放,要么放 \(A\),要么放 \(B\),要么放 \(A+B\)


  • \(50opts\) 的分段

我们可以将 \(DFS\) 转为 \(dp\),同样是四种决策,还可以用滚动数组,然而并没有什么卵用。


  • \(100opts\) 的分段

数论预警!!!

我们可以发现 \(A+B\) 其实可以拆开,当作选一个 \(A\),一个 \(B\)

这样我们可以枚举 \(A\) 的个数 \(i\),每次判断 \(\frac {k} {A}\times i\) 是不是 \(B\) 的倍数,是,\(ans+=C_n^i+C_n^{\frac {\frac{k}{A}*i} {B}}\)

注意要判断特殊情况:

  • \(A=0,B=0,k=0\) 时,\(ans=4^n\)

  • \(A\neq 0,B\neq 0,k=0\) 时,\(ans=1\)

  • \(A=0 / B=0,k=0\) 时,\(ans=2^n\)

  • \(A=0,B=0,k\neq 0\) 时,\(ans=0\)

  • \(A=0 / B=0,k!=0\) 时,设 \(p=\frac{k}{max(A,B)}\)\(ans=C_n^p\times 2^p\times 2^{n-p}=C_n^p\times 2^n\)

代码

50opts dp

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn = 1e7 + 50, INF = 0x3f3f3f3f, mod = 998244353;
inline int read(){
	int x = 0, w = 1;
	char ch;
	for (; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

int n, a, b, c, k;
ll f[2][maxn][4];

ll MOD(ll a, ll b){//快速模
	if(a + b >= mod){
		return a + b - mod;
	}else {
		return a + b;
	}
}

int main(){
	n = read(), a = read(), b = read(), k = read();
	c = a + b;
	f[0][0][0] = 1;
	int s = 0;
	for (register int i = 1; i <= n; i++) {
		s = !s;
		for (register int j = 0; j <= min(i * c, k); j++){
			f[s][j][0] = f[s][j][1] = f[s][j][2] = f[s][j][3] = 0;
		}
		for (register int j = 0; j <= min(i * c, k); j++) {
			f[s][j][0] = (f[s][j][0] + MOD(f[!s][j][0], f[!s][j][1]) + MOD(f[!s][j][2], f[!s][j][3])) % mod;
			if (j >= a) f[s][j][1] = (f[s][j][1] + MOD(f[!s][j-a][0], f[!s][j-a][1]) + MOD(f[!s][j-a][2], f[!s][j-a][3])) % mod;
			if (j >= b) f[s][j][2] = (f[s][j][2] + MOD(f[!s][j-b][0], f[!s][j-b][1]) + MOD(f[!s][j-b][2], f[!s][j-b][3])) % mod;
			if (j >= c) f[s][j][3] = (f[s][j][3] + MOD(f[!s][j-c][0], f[!s][j-c][1]) + MOD(f[!s][j-c][2], f[!s][j-c][3])) % mod;
		}
	}
	printf("%lld\n", MOD(MOD(f[s][k][0], f[s][k][1]), MOD(f[s][k][2], f[s][k][3])));
	return 0;
}

正解

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn = 1e7 + 50, INF = 0x3f3f3f3f, mod = 998244353;
inline int read(){
	int x = 0, w = 1;
	char ch;
	for(; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w; 
}

int n, a, b, k;
int ans;
int jc[maxn + 5], jcny[maxn + 5];

int qpow(int x, int y){//快速幂
	int ans = 1;
	while (y) {
		if (y & 1) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}

int Ny(int x){//费马小定理求逆元
	return qpow(x, mod - 2);
}

void Init(){
	jc[0] = 1;
	for (register int i = 1; i <= n; i++) {//求阶乘
		jc[i] = (jc[i-1] * i) % mod;
	}
}

int C(int a, int b){//组合数的计算
	if (a == 0 || a < b) return 0;
	if (b == 0) return 1;
	else return jc[a] * Ny(jc[b]) % mod * Ny(jc[a-b]) % mod;
}

signed main(){
	n = read(), a = read(), b = read(), k = read();
	if (a == 0 && b == 0 && k != 0) {
		printf("0\n");
		return 0;
	}
	if (a == 0 && b == 0 && k == 0) {
		ans = 1;
		for (int i = 1; i <= n; i++) {
			ans = (ans * 4) % mod;
		}
		printf("%lld\n", ans);
		return 0;
	}else if ((a == 0 || b == 0) && k == 0) {
		ans = 1;
		for (int i = 1; i <= n; i++) {
			ans = (ans * 2) % mod;
		}
		printf("%lld\n", ans);
		return 0;
	}
	Init();
	if (a == 0 || b == 0) {
		int p = k / max(a, b);
		ans = C(n, p);
		for (int i = 1; i <= n; i++) {
			ans = (ans * 2) % mod; 
		}
		printf("%lld\n", ans % mod);
		return 0;
	}
	for (int i = 0; i <= n && a * i <= k; i++) {
		if ((k - a * i) % b == 0 && (k - a * i) / b <= n) {
			int j = (k - a * i) / b;
			ans = (ans + C(n, i) * C(n, j)) % mod;			
		}
	}	
	printf("%lld\n", ans % mod);
	return 0;
}

空间宝石

题目描述

\(zP1nG\) 很清楚自己打不过灭霸,所以只能在自己出的题里欺负他。

咳咳。这一次他得到了空间宝石 \(Tesseract\)

世界之树连接着九界,此时灭霸和 \(zP1nG\) 都在九界的某个地方。而九界是相互无法到达的。\(zP1nG\) 为了追杀灭霸,决定使用空间宝石的力量到达灭霸身边。因为 \(zP1nG\) 不擅长使用空间宝石,无法直接开一条从自己的位置到灭霸的位置的传送门,所以他只能无意识地使用空间宝石的力量。\(zP1nG\) 想知道,在自己胡乱地使用空间宝石后,通过传送门到达灭霸的位置最少需要多长时间。

具体地,九界的编号为 \(0\sim 8\),共有 \(n\) 道传送门,第 \(i\) 道传送门为优先级为 \(p_i\),由 \(u_i\)\(v_i\),需要花费 \(w_i\) 个时间的单向门。传送的规则为:\(zP1nG\) 按顺序穿过的传送门的优先级必须是单调不降的。例如,\(zP1nG\) 穿过了三道传送门到达灭霸的位置,这三道传送门的优先级分别为 \(1→2→2\) 即为一种合法的方式,而优先级分别为 \(1→2→1\) 是不合法的。

\(zP1nG\) 会使用 \(q\) 次宝石的力量来尝试进行传送:其中第 \(i\) 次尝试会打开数道传送门,这些传送门的优先级会形成 \(s_i\) 个区间。例如,在某次尝试中 \(zP1nG\) 打开了三个区间 \([1,2],[4,7],[9,10]\),那么优先级在这三个区间内的传送门将全部被打开并允许 \(zP1nG\) 穿过。你需要告诉 \(zP1nG\) 在此情况下从自己的位置 \(z_i\) 到达灭霸所在处 \(t_i\) 所需的最短时间。尝试结束后所有传送门会关闭。

输入格式

\(1\) 行包含两个正整数 \(n\)\(S\)\(S\) 的含义见数据范围与约定所述。

\(2\)\(n+1\) 行每行包含 \(4\) 个正整数\(p_i,u_i,v_i,w_i\)

\(n+2\) 行包含一个正整数 \(q\)

\(n+3\) 至第 \(n+q+2\) 行每行若干个正整数,其中前三个数为 \(z_i,t_i,s_i\),之后为 \(2\times s_i\) 个正整数,表示每个区间的左右端点。

各变量具体含义见题目描述所述。

输出格式

对于 \(zP1nG\) 进行的每次尝试,输出一行一个数表示从 \(zP1nG\) 的位置到灭霸的位置所需的最短时间,如果 \(zP1nG\) 无法到达灭霸的位置则输出 \(-1\)

样例

样例输入

6 2
1 2 4 1
2 1 3 3
3 1 2 2
4 3 4 5
5 2 4 3
6 1 4 2
4
1 4 1 1 3
1 4 1 1 4
1 4 2 5 5 2 3
1 4 1 1 6

样例输出

-1
8
5
2

数据范围与提示

对于 \(100\%\) 的数据,\(1≤n≤100,000\)\(1≤S≤5\)\(1≤p_i≤2,000\)\(u_i≠v_i\)\(z_i≠t_i\)\(1≤w_i≤100,000,000\)\(1≤∑s_i≤10,000\)

保证每次尝试的区间没有交集,各测试点的约定见下表。

\(S\) 的含义如下:

\(1\):保证 \(u_i,v_i≤1\)

\(2\):保证每个 \(p_i\) 各不相同。

\(3\):保证 \(∑s_i≤50\)

\(4\):保证每个询问区间的左端点 \(=1\)

\(5\):无特殊限制。

注意:整数 \(S\) 只是为了让你更容易地完成子任务,你可能不会用到 \(S\)

思路

很独特的一个思路,我们发现整张图,只有 \(9\) 个点,这么少的节点,我们可以跑最短路中的 \(Floyd\),但是优先级怎么处理呢?

我们可以想到矩阵乘法,不过我们这里不是加和,而是取 \(min\),将同一优先级的最短路存在一个矩阵里。

然后我们可以建一个矩阵的线段树,从小到大存,查询时,注意计算不能够交换,因为矩阵乘不满足乘法交换律。

没啥可讲的,差不多就是线段树板子,将 \(int\) 换成 \(Matrix\)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn = 2e3 + 50, INF = 0x3f3f3f3f3f3f3f3f;
inline int read(){
	int x = 0, w = 1;
	char ch;
	for(; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w; 
}

int n, S, Q;

struct Node{
	int l, r;
}q[maxn];

bool cmp(Node x, Node y){
	return x.l < y.l;
}

struct Matrix{
	int a[10][10];
	Matrix() {
		memset(a, 0x3f, sizeof a);
		for (int i = 1; i <= 9; i++) {
			a[i][i] = 0;
		}
	}
	Matrix operator * (const Matrix &b) const {
		Matrix c;
		for (int k = 1; k <= 9; k++)
			for (int i = 1; i <= 9; i++)
				for (int j = 1; j <= 9; j++)
					c.a[i][j] = min(c.a[i][j], a[i][k] + b.a[k][j]);
		return c;
	}
}a[maxn],tree[maxn<<2];

void Floyd(){
	for (int s = 1; s <= maxn; s++) {
		for (int k = 1; k <= 9; k++) {
			for (int i = 1; i <= 9; i++) {
				for (int j = 1; j <= 9; j++) {
					a[s].a[i][j] = min (a[s].a[i][j], a[s].a[i][k] + a[s].a[k][j]);
				}
			}
		}
	}
}

void Build(int rt, int l, int r){
	if (l == r) {
		tree[rt] = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	Build(rt << 1, l, mid);
	Build(rt << 1 | 1, mid + 1, r);
	tree[rt] = tree[rt << 1] * tree[rt << 1 | 1];				
}

Matrix Query(int rt, int l, int r, int s, int t){
	if (l == s && r == t) {
		return tree[rt];
	}	
	int mid = (l + r) >> 1;
	if (t <= mid) return Query(rt << 1, l, mid, s, t);
	if (s > mid) return Query(rt << 1 | 1, mid + 1, r, s, t);
	return Query(rt << 1, l, mid, s, mid) * Query(rt << 1 | 1, mid + 1, r, mid + 1 , t);
}

signed main(){
	n = read(), S = read();
	for (int i = 1; i <= n; i++) {
		int p = read(), u = read(), v = read(), w = read();
		a[p].a[u + 1][v + 1] = min(a[p].a[u + 1][v + 1], w);		
	}
	Floyd();
	Build(1, 1, maxn);
	Q = read();
	while (Q--) {
		Matrix ans;
		memset(q, 0, sizeof q);
		int u = read(), v = read(), opt = read();
		u++;
		v++;
		for (int i = 1; i <= opt; i++) {
			q[i].l = read(), q[i].r = read(); 
		}	
		sort(q + 1, q + 1 + opt, cmp);//区间排序
		for (int i = 1; i <= opt; i++) {
			ans = ans * Query(1, 1, maxn, q[i].l, q[i].r);
		}
		if (ans.a[u][v] == INF) {
			printf("-1\n");
		}else {
			printf("%lld\n", ans.a[u][v]);
		}
	}	
	return 0;
}
posted @ 2020-08-05 07:13  Rubyonlу  阅读(293)  评论(0编辑  收藏  举报