大假期集训模拟赛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;
}