首页 > 其他分享 >高一下三调|ssy的队列|hash dp|题解

高一下三调|ssy的队列|hash dp|题解

时间:2024-05-05 20:33:35浏览次数:67  
标签:ssy int 题解 ll 状压 long hash dp

image
image
SSY的队列题目链接

解析:

考场上看到这个题第一眼是绝望的,毕竟数论咱是一窍不通.
但是往下细看了看这个数据范围,N是很小的,就想了想模拟.
然而只骗到10分.
这个题绩效较高的解法是状压dp,在N<15的范围之内均可薄纱(ppllxx_9G也是成功拿到这70 rank 1了 orz),可得70,但是一到后面三个点就是RE了.
虽说非正解,但也是借这个题又回顾了一下我们的状压dp.
状压代码:

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

inline ll qr
{
	ll x=0;char ch=getchar();bool f=1;
	while(ch>57||ch<48)
	{
		if(ch=='-')f=0;
		ch=getchar();
	}
	while(ch>=48&&ch<=57)x=(x<<3)+(x<<1)+ch-48,ch=getchar();
	if(f)return x;
	else return ~(x-1);
}
const int mod=1234567891;
ll n,m,num[31],f[1<<17][17],ans;
bool vis[17][17];
void init()
{
	n=qr;
	for(int i=1;i<=n;++i)
		num[i]=qr;
	putchar('\n');
	m=qr;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<i;++j)
		{
			if(abs(num[i]-num[j])%m)vis[i][j]=vis[j][i]=1;
		}
	}
	for(int i=0;i<n;++i)
		f[(1<<i)][i]=1;
	for(int i=1;i<(1<<n);++i)
	{//状压枚举状态 我们每次都是在整个状态的末尾加新的人 
		for(int now=0;now<n;++now)
		{//枚举站在最末尾的人 
			if((1<<now)&i)
			{//这个人在状态内 
				for(int man=0;man<n;++man)
				{//枚举新加入的人 
					if(((1<<man)&i));
					else if(vis[man+1][now+1])
						f[i|(1<<man)][man]=(f[i|(1<<man)][man]+f[i][now])%mod;
				}
			}
		}
	} 
	for(int i=0;i<n;++i)
		ans=(ans+f[(1<<n)-1][i])%mod;
	printf("%lld",ans);
}
int main()
{
	freopen("ssy.in","r",stdin);
	freopen("ssy.out","w",stdout); 
	init();
	return 0;
} 

正解思路十分奇妙,我们观察题目的描述,意思是所有对M余数相等的数是不可放在一起的.
那么我们可以将所有的数以余数分类,再进行处理.
可知:
1.同类数不可以放在一起,但是同类数的位置均可对调.
2.我们定义的某个状态的种类数,事实取决于某个状态不同种类的不同剩余个数的情况.
第二条是较难归纳出的,我们来看一个浅显的例子:
2233这四个数进行排列的种类数与4455这四个数排列的情况数是一致的.
状压为什么会炸,因为它存了很多没有用的状态而且也访问并转移了许多没有用的状态.
同时因为它没有类别之分,就会导致很多时候它的一些状态是已经访问过的,但状压无法辨识.
如此看状压已经没有优化空间,但是状压的思路我们要留着.
接着看如何实现.
我们根据上述规律去走一遍dfs,只不过这次不是单纯的暴力模拟了,我们加入了记搜,状压,以及hash成分.
为什么还有hash呢,因为我们想想,dfs依旧以着dp的思路在跑.
然而我们的dp描述状态要各个数余下的种类以及最后一个数是哪一种的(因为明显我们是不可以把两个同种的放在一起的).
但是它有三十个数,所以明显咱们不能开三十维.
那就要借助一个hash的思想,将状态压成一个ull的数,同时用map将这个数映射到一个可以操作的下标上.
这样就能保证转移有效,省去空间,同时得到正解.

正解:

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

const int N=50,mod=1234567891,mode=131;
//        种类数↓  ↓每个种类中所含数. 
int n,num[N],m,tot,all[N],nowleft[N],mx;
//                           ↑未被操作的剩下个数为相应大小的种类的个数. 
bool vis[N];
map <llu,ll> mp[N];//用map实现记忆化,保证重复状态不重搜. 
ll ans,pw[N];
//                 ↓最后一个数是哪一种的. 
ll dfs(int now,int lst)
{//        ↑填到第几个数 
	if(now>n)//边界是所有数都已填入. 
		return 1;
	memset(nowleft,0,sizeof(nowleft));
	for(int i=1;i<=tot;++i)
		if(i^lst)++nowleft[all[i]];
	llu nzt=all[0];
	for(int i=1;i<=mx;++i)//个数为0的种类不必存入状态中. 
		nzt=nzt*mode+nowleft[i];
	nzt=nzt*mode+all[lst]; 
	if(mp[now].find(nzt)!=mp[now].end())
		return mp[now][nzt];//已经访问过的状态不再搜. 
	ll nans=0;
	if(all[0])
	{
		--all[0];
		nans=(nans+dfs(now+1,0))%mod;
		++all[0];
	}
	for(int i=1;i<=tot;++i)
	{
		if((i^lst)&&all[i])
		{
			--all[i];
			nans=(nans+dfs(now+1,i))%mod;
			++all[i];
		}
	}
	return mp[now][nzt]=nans;//记忆化 
}
void init()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&num[i]);
	}
	scanf("%d",&m);
	for(int i=1;i<=n;++i)
		num[i]=(num[i]%m+m)%m;
	for(int i=1;i<=n;++i)
	{
		if(vis[i])continue;
		vis[i]=1;
		int cnt=0;
		for(int j=i;j<=n;++j)
		{
			if(num[i]^num[j]);
			else vis[j]=1,++cnt;
		}
		mx=max(mx,cnt);
		if(cnt==1)++all[0];
		else all[++tot]=cnt;
	}
	pw[0]=1;
	mx=max(all[0],mx);
	for(int i=1;i<=mx;++i)
		pw[i]=pw[i-1]*i%mod;
	ans=1;
	for(int i=0;i<=tot;++i)
		ans=ans*pw[all[i]]%mod;
	ans=ans*dfs(1,0)%mod;
	printf("%lld",ans);
}
int main()
{
	freopen("ssy.in","r",stdin);
	freopen("ssy.out","w",stdout); 
	init();
	return 0;
}

标签:ssy,int,题解,ll,状压,long,hash,dp
From: https://www.cnblogs.com/shining-like-stars/p/18172448

相关文章

  • AtCoder Beginner Contest 352题解
    AtCoderBeginnerContest352Time:2024-05-04(Sat)20:00-2024-05-04(Sat)21:40AAtCoderLine问题陈述AtCoder铁路线有$N$个车站,编号为$1,2,\ldots,N$。在这条线路上,有趟进站列车从$1$站出发,依次停靠$2,3,\ldots,N$站,有趟出站列车从$N$站出发,依次停......
  • P3193 [HNOI2008] GT考试 题解
    之前学矩阵乘的时候做的题,当时因为不会\(kmp\)搜索一稀里糊涂过去了,现在填个坑。头图是\(Logos\)!P3193[HNOI2008]GT考试题链:洛谷题库题目大意:求有多少个长度为\(n\)的数字串的子串中不包含给出的长度为\(m\)位的串,范围\(n<=1e9\),$m<=20$。思路:首先考虑DP,令\(......
  • CF1630A And Matching 题解
    题目描述有\(n\)个数\(0,1,2,\cdots,n-1\)。你需要把他们两两分组,使得每组两个数按位与的结果之和\(=k\)。如果可能,请构造出一组可能的\(\fracn2\)个数对,否则输出-1。保证\(n\)是\(2\)的幂,\(k\len-1\)思路首先我们发现,\(n\)是二的幂,所以按照二进制的角度看,这......
  • [SDOI2015] 星际战争 题解
    假如将所有激光武器放在一边,所有机器人放在一边,激光武器向它可以伤害的机器人连边,再加超级源/汇点,这就是一个网络流问题。考虑激光武器向机器人连的边容量无限,而机器人向超级汇点连的边容量为机器人的装甲值,而超级源点连向激光武器的边则是用时\(\times\)激光武器伤害。发现假......
  • CF729B Spotlights 题解
    题目简述给出一个$n$行$m$列的$01$矩阵,定义每个点的价值为上下左右四个方向有$1$的方向数,求所有为$0$的点的价值和。题目分析我们首先可以考虑暴力,但是发现是不行的。于是我们考虑动态规划。设$dp_{i,j,0/1/2/3}$分别表示$(i,j)$这个点上方,左方,下方,右方是否有$......
  • [SCOI2007] 蜥蜴 题解
    发现实际上就是在求有多少只蜥蜴能逃出来。发现可以将柱子拆成入点和出点两部分,自己的出点向别人的入点连边,自己的入点向自己的出点连边。最后再加一个超级源点\(S\),连接所有有蜥蜴的柱子入点;再加一个超级汇点\(T\),连接所有能够跳出地图的柱子。我们猛然发现:这个问题不就是求......
  • 题解:AT_abc352_C [ABC352C] Standing On The Shoulders
    考场憋了很久,最后代码贼短……理想状态下,直接全排列解决问题。但是,\(1\len\le2\times10^5\),明显TLE,试都不用试的。咋优化呢?其实,前面的巨人只负责“打地基”,作为“塔尖儿”的巨人有且仅有1个。而前面地基随便排列,地基高度(他们的和)都不会变。所以,我们只需要枚举塔尖即......
  • 牛客小白月赛92 题解
    牛客小白月赛92题解A.获得木头签到\((x\times4)/2\times4=x\times8\)#include<bits/stdc++.h>usingnamespacestd;#definefffirst#definesssecond#definepbpush_back#defineall(u)u.begin(),u.end()#defineendl'\n'#definedebug(x......
  • 牛客 215E 黄魔法师 题解
    Description给出\(n,k\),求一个长度为\(n\)的数组\(a\),满足有恰好\(k\)对数对\((i,j)(1\leqi<j\leqn)\)满足\(a_i+a_j\)为完全平方数。如果不存在,输出\(-1\)。linkSolution显然如果\(k>\binom{n}{2}\)就一定无解。构造时会发现肯定要尽量弄成相同的......
  • 题解【[ABC155F] Perils in Parallel】(未完成)
    题目链接两个常规转化:灯的坐标与区间坐标都很大,不妨将其离散化,转化为\(1\simn\)的点与\(1\simn\)的操作区间。对于一段区间取反,可以理解为对一段区间异或\(1\),转化为在异或差分数组上操作,即差分数组\(diff_i=a_i\bigoplusa_{i-1}\),区间\([l,r]\)异或\(1\)转化为差......