首页 > 其他分享 >Atcoder ABC388F Dangerous Sugoroku 题解 [ 蓝 ] [ 矩阵加速 ] [ 状压矩乘 ] [ 模拟 ]

Atcoder ABC388F Dangerous Sugoroku 题解 [ 蓝 ] [ 矩阵加速 ] [ 状压矩乘 ] [ 模拟 ]

时间:2025-01-12 13:33:03浏览次数:1  
标签:Atcoder 20 mat dp1 qpow int 题解 Sugoroku res

Dangerous Sugoroku:赛时写了矩乘 T 飞了,受到 sunkuangzheng 大佬的启发才知道要状压矩乘。

暴力矩乘思路

直接像过河那样写模拟细节非常多,于是考虑像美食家一样的思路,利用矩阵分段加速。

定义 \(dp_i\) 表示 \(i\) 能否到达,则有如下转移:

\[dp_{i}=\bigvee_{j=i-B}^{i-A}dp_{j} \]

因为 \(A,B\le 20\),所以可用状态非常少,就可以用矩阵优化了。

那么很显然就把段拆一下,然后直接跑矩乘就好了。这个做法甚至连 \(m=0,l_i-1=r_{i-1}\) 的细节都不用判。

代码如下,如果你真这么写了,会收获 TLE 和 WA 的好成绩!

下面这份暴力中 \(dp_i\) 表示 \(dp_i\) 的方案数,所以转移有所不同,本质还是和上面一样的。只要 \(dp_i>0\) 就能到达。

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
struct mat{
	ull a[25][25];
	mat(){memset(a,0,sizeof(a));}
	mat operator*(const mat &x)const{
		mat res;
		for(int i=0;i<=20;i++)
		{
			for(int k=0;k<=20;k++)
			{
				ull l=a[i][k];
				for(int j=0;j<=20;j++)
				{
					res.a[i][j]=(res.a[i][j]+l*x.a[k][j]);
				}
			}
		}
		return res;
	}
}s,dp1,dp2;
mat qpow(mat x,ll k)
{
	mat res;
	for(int i=0;i<=20;i++)res.a[i][i]=1;
	while(k>0)
	{
		if(k&1)res=res*x;
		x=x*x;
		k>>=1;
	}
	return res;
}
ll n,m,a,b,l[200005],r[200005];
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>a>>b;
    s.a[1][20]=1;
    for(int i=1;i<=19;i++)dp1.a[i+1][i]=dp2.a[i+1][i]=1;
    for(int i=20-a+1;i>=20-b+1;i--)dp1.a[i][20]=1;
    r[0]=1;
    for(int i=1;i<=m;i++)
    {
        cin>>l[i]>>r[i];
        if(r[i]-l[i]+1>=21||l[i]==1)
        {
            cout<<"No";
            return 0;
        }
        if(l[i]-r[i-1]-1>0)s=s*qpow(dp1,l[i]-r[i-1]-1);
        if(r[i]-l[i]+1>0)s=s*qpow(dp2,r[i]-l[i]+1);
    }
    if(n+1-r[m]-1>0)s=s*qpow(dp1,n+1-r[m]-1);
    if(s.a[1][20])cout<<"Yes";
    else cout<<"No";
    return 0;
}

原因是复杂度为 \(O(m\times 20^3 \log n)\),常数又大,无法通过。

优化矩乘

因为状态中只有 \(0\) 和 \(1\),每行每列的数又只有 \(20\) 个,考虑用状压与位运算去掉一个 \(20\)。

我们先看暴力的矩乘代码,再来考虑如何优化它。

struct mat{
	ull a[25][25];
	mat(){memset(a,0,sizeof(a));}
	mat operator*(const mat &x)const{
		mat res;
		for(int i=0;i<=20;i++)
		{
			for(int k=0;k<=20;k++)
			{
				for(int j=0;j<=20;j++)
				{
					res.a[i][j]=(res.a[i][j]|(a[i][k]&x.a[k][j]));
				}
			}
		}
		return res;
	}
}

可以发现,当 \(a_{i,k}=1\) 时,\(res_{i,j}\) 取决于 \(x_{i,j}\) 是否为 \(1\)。

因此,就可以省去循环 \(j\),当 \(a_{i,k}=1\) 时,直接让 \(res_i \gets res_i \vee x_k\) 即可。

时间复杂度 \(O(m\times 20^2 \log n)\),可以通过。

代码如下:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
struct mat{
	ull a[25];
	mat(){memset(a,0,sizeof(a));}
	mat operator*(const mat &x)const{
		mat res;
		for(int i=0;i<=20;i++)
		{
			for(int k=0;k<=20;k++)
			{
				if((a[i]>>k)&1)res.a[i]=(res.a[i]|x.a[k]);
			}
		}
		return res;
	}
}s,dp1,dp2;
mat qpow(mat x,ll k)
{
	mat res;
	for(int i=0;i<=20;i++)res.a[i]=(1<<i);
	while(k>0)
	{
		if(k&1)res=res*x;
		x=x*x;
		k>>=1;
	}
	return res;
}
ll n,m,a,b,l[200005],r[200005];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>a>>b;
    s.a[1]=(1<<20);
    for(int i=1;i<=19;i++)dp1.a[i+1]=dp2.a[i+1]=(1<<i);
    for(int i=20-a+1;i>=20-b+1;i--)dp1.a[i]|=(1<<20);
    r[0]=1;
    for(int i=1;i<=m;i++)
    {
        cin>>l[i]>>r[i];
        if(r[i]-l[i]+1>=21||l[i]==1)
        {
            cout<<"No";
            return 0;
        }
        if(l[i]-r[i-1]-1>0)s=s*qpow(dp1,l[i]-r[i-1]-1);
        if(r[i]-l[i]+1>0)s=s*qpow(dp2,r[i]-l[i]+1);
    }
    if(n+1-r[m]-1>0)s=s*qpow(dp1,n+1-r[m]-1);
    if((s.a[1]>>20)&1)cout<<"Yes";
    else cout<<"No";
    return 0;
}

标签:Atcoder,20,mat,dp1,qpow,int,题解,Sugoroku,res
From: https://www.cnblogs.com/zhr0102/p/18666901

相关文章

  • 买房子题解
    【题目要求】问第几年能够买下这套房子。一、建立两个变量,一个表示积攒的钱数,一个表示房价。二、循环20次,判断第i年他能否买下,若能,那么输出i,最后return0。三、在循环外面,输出Impossible。【题解代码】include<bits/stdc++.h>usingnamespacestd;intmain(){double......
  • 整数序列的元素最大跨度值题解
    【题目要求】计算序列的最大跨度值(最大值-最小值)一、求最大值如果a大于最大值,那么最大值就变成a,开始最大值要等于0。二、求最小值如果a小于最小值,那么最小值就变成a,开始最小值要等于1000。【题解代码】include<bits/stdc++.h>usingnamespacestd;intmain(){intn,a,......
  • Atcoder ABC387F Count Arrays 题解 [ 绿 ] [ 基环树 ] [ 树形 dp ] [ 前缀和优化 ]
    CountArrays:一眼秒的计数题。思路显然,把小于等于的条件化为大的向小的连单向边,每个数的入度都是\(1\),就会形成一个基环树森林。那么考虑这个环上能填什么数。因为所有数都小于等于他后面的数,所以所有数都只能相等。这就启发我们在基环树上缩点之后再进行计数。那么当缩完点......
  • 题解:AT_abc388_g [ABC388G] Simultaneous Kagamimochi 2
    鉴于本题解书写时洛谷题面暂无中文翻译,为避免可能的歧义或困惑,先对本题解中的译法进行约定:(顺便吐槽音译怪)英文题面中“mochi”或日文题面中“餅”译为“饼”。英文题面中“kagamimochi”或日文题面中“鏡餅”译为“镜饼”。鉴于本题是C和E的加强版,而我懒得去写那两题的题......
  • AtCoder Beginner Contest 388 (abc388) 赛后复盘
    为什么不会E????A-B模拟即可。C每一个大麻薯可以和所有小于等于自己\(\frac12\)的麻薯结合,二分即可。时间复杂度\(O(n\logn)\)。点击查看代码#include<bits/stdc++.h>#definelllonglong#definei128__int128#definemem(a,b)memset((a),(b),sizeof(a))#def......
  • 2025/1/11 第25场蓝桥入门赛 题解
    A: 哪来的AC  :  题目链接水题:31画#include<iostream>usingnamespacestd;intmain(){//请在此输入您的代码cout<<31;return0;}B: 酒店安排  : 题目链接 思路:从大到小排序,求每两个相邻房间的差值 ,滑动窗口求m-1个差值最小,即为答案要想最......
  • 题解:P1970 [NOIP2013 提高组] 花匠
    闲话本文同步发布在cnblogs。正题容易发现此题要求花必须一高一低摆放。最优化问题,看不出怎么贪心,遂DP。设计状态\(f_{i,0}\)表示当前为上升形势最长花序列,\(f_{i,1}\)表示当前为下降形势最长花序列。状态转移由于需要一高一低,易得:\[f_{i,0}=\begin{cases}......
  • HHKB Programming Contest 2025(AtCoder Beginner Contest 388)
    A-?UPC题意:给你一个字符串,把他的第一个字符和"UPC"输出。输出即可。点击查看代码voidsolve(){std::strings;std::cin>>s;std::cout<<s[0]<<"UPC\n";}B-HeavySnake题意:n条蛇由厚度和长度,重量为厚度乘长度,问长度加上1~k时,最大的蛇的重量分别......
  • P3247 [HNOI2016] 最小公倍数 题解
    \(\text{P3247[HNOI2016]最小公倍数题解}\)第一眼上去没什么明显的思路。图上问题一般没有什么好的多项式复杂度算法来解决。观察数据范围,注意到\(n,q\le5\times10^4\),是一个典型的根号复杂度算法,于是考虑分块来处理。注意到所求的不一定是简单路径,也就是在不超过所需要的......
  • P10200 花神诞日 题解
    P10200[湖北省选模拟2024]花神诞日题解首先注意到一个集合中两两异或和的最小值就是,排序后相邻两个数异或和的最小值。证明可以考虑放到01-Trie上,从高往低位建树,求一个数与之异或的最小值,就是使高位相同位数尽可能多,则就是01-Trie上的前一个叶子或后一个叶子。由此,我们......