NOI2016~2021 Solution Set

口胡一张嘴。

28/36。

NOI2016

优秀的拆分

极限得分:95(100?)。

AABBAB 间计数,然后相当于求 fi,gi 分别表示以第 i 个字符结尾有多少个 AAgi 类似。

枚举 |A|,然后在 |A|,2|A|, 放置分割点,容易发现 AA 过两个分割点。然后查 LCP 或 LCS 后缀数组解决。

网格

极限得分:92(写挂了)。

答案显然小于等于 2

判断 1,0 比较显然,然后现在是找整个图里面有没有割点。我们选取一些怪七八糟的关键点然后 Tarjan 就好了。主要是没有证明很麻烦,具体是如果该行有蛐蛐,那就正上下左右以及与之八联通的格子看成关键点,然后在选取所有的四个角的八联通格子当成关键点。

建图就 x,y 相同的建一块儿就好了。证明我不会,反正过掉了 =.=

循环之美

极限得分:100。

简单莫比乌斯反演。规律显然。主要是不想写式子,后面补吧。这个题在莫比乌斯反演一套里面算很简单的了。

算了写一发,理性猜测选出来的分数 ij 要满足 jkji,证明显然(除了相当于小数点移位了)。那就化一波式子,中间那些杂七杂八的不要了,就是:

dμ(d)ndj=1m/d[jdk]

把里面的 [jdk] 拆成 [jk][dk],并把只跟 d 有关的东西提出来。

那里面那坨东西非常好算只需要预处理 O(k) 项。已经是一个类似于整除分块的形式。把不能整除分块的东西提出来做这个。总之可以拆成一个可以递归的形式,然后上杜教筛求 μ 的前缀和。

吐槽,这种题出出来不是给差点金哥哥送分的?

区间

极限得分:100。

大概是说因为只跟最大的和最小的有关,我们将区间长度从大到小排序,选出来的区间定然是一段。离散化后线段树维护区间的覆盖,双指针移动就好了。

国王饮水记

极限得分:85。

每次操作显然必带结点 1,不然没有意义。

然后显然我们每次只能操作比当前更大的,可以得到一个 O(n2kp) 的 DP。当然你可以暂时先不管精度,然后倒推,可以做到 O(n2k),其中 k 的级别可以到 O(n)(不妙啊!这样就可以 85 了)。

这个 DP 不好优化,注意到每次选两个比较优,猜测选的大段个数和长度不会很大。那么先用上面不管精度最后倒推的方法,然后设个一个好的转移阈值(转移区间最大长度,之类的)就好了,总之能过。

旷野大计算

极限得分:40(吧?)。

我再也不抄题解了,对不起。这个题太牛逼了,找个时间好好做……

NOI2017

真是奇怪啊,为什么这场我是真的一个题也不会啊(划掉)

整数

极限得分:44(你妈啊,我是真不会啊)。

正负拆开只需要比较前 k 位大小暴力加均摊复杂度正确,压位计算就好了。

话说得好听啊,但是我怎么觉得这个狗题这么难写??

蚯蚓排队

极限得分:100(?有点大并。)

直接大暴力就好了…… 时间复杂度是 O(mk2) 的,哈希就好了。

读题是难点。

泳池

极限得分:25(15?)。

不会,不会就是不会,怎么学也不会。

先容斥掉,小于等于 K 的减去小于 K 的。不用大于是因为上限有点太高了。

【等待施工中……】

游戏

极限得分:100。

到达 NOI2017 最垃圾题,游戏!太垃圾啦游戏。哎呀这不 2-SAT 吗,还是枚举一下 x 是什么吧家人们。

好吧也许蚯蚓排队最垃圾,但是差不多嘛。枚举 x 之后每个位置只有两种选择…… 做做就好了。

蔬菜

极限得分:80+12(?)(部分分真的送的很多吗?纯纯的口胡),有点病,我突然觉得我能过。

显然倒推就可以直接硬贪心,这个是普及组问题。现在有多组,因为这是个普及组问题,你要从 day p 到 day p1,就是直接挑 m 个收益最小的菜不要就好了……

然后似乎有个不用倒推的并查集贪心做法,非常的 exciting 啊!

NOI2018

归程

极限得分:100。

先对水位线从大到小排序建一个 Kruskal 重构树,然后求出 1 到其他的点的最短路,然后树上倍增找到可以到达的所有点(容易发现构成一棵子树),找个最小值就好了。

冒泡排序

极限得分:72(数数水平低于人均 T^T)。

显然达到下界就是一个数不会往左移了往右移。如果出现一个长度为 3 的下降子序列就非常的不优秀(假设为 a,b,c,在动 a 的时候 b 向左,动 b 的时候一定会和 c 交换)。

Dilworth 定理告诉我们最长链长度等于最大反链独立集,也就是有最多两个上升子序列。记 dpi,j 表示选了 i 个数,最大值为 j,那么向后要么加小于 j 的最小数,要么加大于 j 的数。

容易得到一个 O(n3) 的 DP 转移,前缀和优化之后可以得到 dpi,j=dpi1,j+dpi,j1 的转移。然后为了下面的分析,我们令 dpn,n1,转移改为 dpi,j=dpi+1,j+dpi,j+1

然后注意到 i>j 的时候 dpi,j0。这个形式类似于网格图,每次只能向下或者向左动,不能越过 y=x 这条线。

考虑计数。我们假设前 i1 位和 q 一样,并且选出的最大值为 v,没选到的最小值为 u

在选择第 i 位的时候,因为字典序要严格大于 q,因此第 i 位的数 k>max(qi,v)。那么相当于有初始值 dpi,max(qi,v)+1n1。前缀和优化到 dpi1,max(qi,v)+11。用网格图的角度就是 (n,n)(i1,max(qi,v)+1) 并且不越过 y=x。简单分析就好了,这个问题很经典。

然后如果 u<qi<v 说明后面已经没有前途了!退出吧,再算就错了。否则显然后面还能继续填。

你的名字

极限得分:68(字符串一点也不会)。

试图建出 s[lr] 的后缀自动机。先建出 s 的后缀自动机,后缀树部分和自动机转移部分显然不变,要变的只有 endpos。

那就线段树合并表示出 endpos,然后就是近似 l=1,r=|s| 的情况,暴力匹配就好了。不难写。

另外算答案,注意到答案是 t 的本质不同子串个数,减去 s[lr]t 的本质不同公共子串个数。那么保证每次减去的都是本质不同的就好了…… 也就是在第 i 个位置的时候,匹配长度为 pt 的后缀自动机上新增的结点的父亲结点表示的最大长度为 q,贡献就是 imax(p,q)

屠龙勇士

极限得分:100。

就是简单魔改 EXCRT,乱写就好了。

NOI2019

回家路线

极限得分:100(真的不是 O(mt) 吗)。

哦还真不是。虽然就也很,也许很简单吧,直接斜优!

好吧我过不了加强版。

序列

极限得分:64。

很久以前以为是个牛逼题,现在看比较傻…… 大概是说你先费用流建图建出来,可以直接反悔贪心做,分直接选一个 Ai,Bi,退一个 A 部点往虚点 A 的流,退一个虚点 BB 部点的流。容易发现每次这种操作会让相同的下标多至少一个,先选出大的 KL 个,再做 L 次上述过程就好了。遗憾的是口胡一张嘴啊,写不出来 100。

弹跳

极限得分:100。

k-d tree 优化建图,非常板子。

斗主地

极限得分:40。

前面的暴力和正解几乎没有关系。有个牛逼结论是一次函数洗牌之后还是一次函数,二次函数洗牌之后还是二次函数……如果有研究一下样例的意识就可以把结论猜出来了。

I 君的探险

极限得分:36(正在做)。

Subtask 1, 2:直接暴力。

Subtask 3:二进制分组。

NOI2020

美食家

wi 很小,拆点。不要拆边,拆点就好。

图建出来,走到第一个点加一点愉悦值,写成一个类似于矩阵的形式,转化成 (max,+) 的广义矩阵乘法。美食节直接枚举就好了,中间可以预处理 2k 的矩阵。

命运

先咕一下……之后重写一遍。

制作菜品

n=m 很萌萌。

m=n1 也很萌萌阿。

然后 m=n2 就不可爱了。我们想转化成两个 mi=ni1 的形式这样就有解了,那就是看有没有 S 使得 iSdi=(|S|1)k。左右两边同时减 |S|k 之后就是 iSdik=k,这是 01 背包可以 bitset 优化。

老实说我觉得这个题挺简单阿,嗯哼?

超现实树

当初有点傻逼,写的题解也很蠢蛋阿。

先分析出如果每个点两个子树的大小 min>1 那么这个树无用的结论(我可以缩小,然后得到你没有的东西,是个天缺)。

然后有一个小 case 是单独一个根结点,和根结点左接一个点右边不接,根结点右接一个点左边不接,两边都接一个点等价。容易发现一个点是完备的,当且仅当:

  • 存在一棵有用的树其是光秃秃的一个结点挂在这里;
  • 下面四个情况同时满足:
    • 有一棵有用的树,其右儿子是完备的,左子树为空;
    • 有一棵有用的树,其左儿子是完备的,右子树为空;
    • 有一棵有用的树,其右儿子完备,左子树大小为一;
    • 有一棵有用的树,其左儿子完备,右子树大小为一。

暴力判断就好了。

NOI2021

应该是自 16 年以来最简单的一套。

轻重边

看起来可以用 LCT,实际确实可以,这是解法一。

解法二是像雨兔一样现场研究毛毛虫,但是这个代价可能比较高昂。

解法三就是普通树剖,但是 motivation 比较奇怪。

注意到路径上的所有点与其连接的所有边全变轻,考虑用颜色这样一个概念去描述。一个边是重的当且仅当两点颜色相同。这样就是链染色问题,整个问题变成了一个原题叫做 SDOI2011 染色,注意处理没染色的情况。这是个经典问题,重链线段树轻边暴力记下链头是什么就好了。

路径交点

Binet-Cauchy 定理简单运用。就是一堆矩阵乘起来求个行列式。

当然也可以 LGV。

关于动机的话,研究 k=2(就是直接求行列式的 case)之后这个题就是萌萌题了。(至于为什么乘起来,可以看这个)。

庆典

先缩点。现在是个外向树。

k=0 是萌萌。

k=1 的话,uvuu1v1v 两条路径。如果说 uv1 子树内那还要多一个 v1u。可以用树剖打标记,下面都这么处理,每次清空就好。

k=2 按照上面的模式,如果存在一条 uv 的路径,接下来的工作就只有找环。首先如果每条路径可以通过怪七八糟的手法可以从 u/v 重复到达 u/v 那路径就是好的,然后好的路径之间还可以有环。

				st=r[0]=r[1]=false;
				if(In(u,v))	st=true,modifyChain(u,v);
				for(int i=0;i<=1;++i)
				{
					if(In(u,adu[i]) && In(adv[i],v))	modifyChain(u,adu[i]),modifyChain(adv[i],v),r[i]=st=true;
					if(In(u,adu[i]) && In(adv[i],adu[i^1]) && In(adv[i^1],v))	modifyChain(u,adu[i]),modifyChain(adv[i],adu[i^1]),modifyChain(adv[i^1],v),r[i]=r[i^1]=st=true;
				}
				if(st)
				{
					for(int i=0;i<=1;++i)
					{
						if(In(v,adu[i]) && In(adv[i],u))	modifyChain(v,adu[i]),modifyChain(adv[i],u),r[i]=true;
						if(In(u,adu[i]) && In(adv[i],u))	modifyChain(adu[i],adv[i]),r[i]=true;
						if(In(v,adu[i]) && In(adv[i],v))	modifyChain(adu[i],adv[i]),r[i]=true;
						if(In(v,adu[i]) && In(adv[i],adu[i^1]) && In(adv[i^1],v))	modifyChain(v,adu[i]),modifyChain(adv[i],adu[i^1]),modifyChain(adv[i^1],v),r[i]=r[i^1]=true;
						if(In(v,adu[i]) && In(adv[i],adu[i^1]) && In(adv[i^1],u))	modifyChain(v,adu[i]),modifyChain(adv[i],adu[i^1]),modifyChain(adv[i^1],u),r[i]=r[i^1]=true;
						if(In(u,adu[i]) && In(adv[i],adu[i^1]) && In(adv[i^1],u))	modifyChain(u,adu[i]),modifyChain(adv[i],adu[i^1]),modifyChain(adv[i^1],u),r[i]=r[i^1]=true;
					}
					for(int i=0;i<=1;++i)
					{
						if(r[i] && In(adv[i],adu[i^1]) && In(adv[i^1],adu[i]))	modifyChain(adv[i],adu[i^1]),modifyChain(adv[i^1],adu[i]);
						if(r[i] && In(adv[i],adu[i]))	modifyChain(adv[i],adu[i]);
					}
				}

量子通信

妙妙题,当初同步赛遗憾题。

k 很小,考虑将 256 位二进制数变成 16216 进制数。

因为 k15,所以如果可能在字典出现至少有一位是一样的。

因为数据随机所以期望复杂度正确啊。虽然我觉得有点小小的搞笑。

密码箱

当初我是小丑吗??

首先根本不需要管最简这个条件,因为上下一定互质。

然后是,将分母上下看成矩阵,密码箱的操作就像是在做线性变换,每次右乘一个矩阵。

然后对每个操作构造一个矩阵然后插在后面,这个矩阵非常好构造我就稍微摆了,W[1101]E 在两种情况下都是 [2110],原来的线性变换是 [a110]

想不到吧,这是个构造题

机器人游戏

首先 O(nm2n) 的容斥写在样例里面了。

然后想平衡复杂度一脸折半。枚举钦定的最右边的指针 r,不会爆炸的机器人位移只有 nr。DP 定义为当前处理了前 i 个位置(每加一个位置容斥系数会多一个 1),在包括当前点的长为 nr范围外有没有钦定的位置(要看后面的是不是一定有一个不变在),然后最近的 nr 个位置钦定的状态为 S。在 r 很小的时候也就 r2r,在 r 很大的时候是 r2nr,这样复杂度是平衡的。

然后要考虑的是转移的系数以及 r 后面的元素。先考虑第一个问题,我们要求某个位置系数为 1,2,3 的机器人的个数(一个位置同时有状态 0,12,3;一个位置同时有 0,1 中的一个或 2,3 中的一个;系数为 3 的可以算有效机器人减去用掉的)。先用 bitset 存下以任意 s 为起点,使得第 s+i 个位置的状态是 0/1/2/3(不变/变成相反/变成 0/变成 1)的机器人的位置状态。

那么首先如果当前在算的位置 p 不是 r(在这里等价于小于 r,因为 r 显然使得 p 不变)或者是在 nr 范围外有钦定的点,那么当前所有的机器人都会造成一个不变,比较显然;然后是可以算在这 nr 个位置里面,所有被钦定的点的状态,使得当前这个位置变成状态 0/1/2/3 的机器人集合。这样分类讨论取个并就好了。

然后是处理 r 之后的元素,被限制的位置同样可以采用上面的方法解决,也就是枚举离 r 的距离 s,然后枚举状态算出使 s 的状态是 0/1/2/3 的机器人集合,又是一顿算就好。

这个题代码太你妈抽象了,,,,所以我贴份代码。代码很清楚阿,要仔细看看。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double DB;
typedef unsigned long long ULL;
const int MOD=1e9+7;
inline int Add(int u,int v){return u+v>=MOD?u+v-MOD:u+v;}
inline int Sub(int u,int v){return u-v>=0?u-v:u-v+MOD;}
inline int Mul(int u,int v){return LL(u)*LL(v)%MOD;}
inline int add(int &u,int v){return u=Add(u,v);}
inline int sub(int &u,int v){return u=Sub(u,v);}
inline int mul(int &u,int v){return u=Mul(u,v);}
int QuickPow(int x,int p=MOD-2)
{
	if(p<0)	p+=MOD-1;
	int ans=1,base=x;
	while(p)
	{
		if(p&1)	ans=Mul(ans,base);
		base=Mul(base,base);
		p>>=1;
	}
	return ans;
}
inline int lowbit(int x){return x&(-x);}
inline int popcount(int x){int ret=0;while(x)	++ret,x^=lowbit(x);return ret;}
typedef bitset<1005> BT;
BT F[(1<<17)][4];
BT Cv[35][4];
BT US;
int n,m;
char str[1005][105];
int v[1005][105],len[1005],mv[1005];
int dp[35][2][(1<<17)];
int pw2[1005],pw3[1005];
vector<int> uid[100];
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;++i)	scanf("%s",str[i]+1),len[i]=strlen(str[i]+1);
	pw2[0]=pw3[0]=1;
	for(int i=1;i<=m;++i)	pw2[i]=Add(pw2[i-1],pw2[i-1]),pw3[i]=Add(Add(pw3[i-1],pw3[i-1]),pw3[i-1]);
	for(int i=1;i<=m;++i)
	{
		for(int j=1;j<=len[i];++j)
		{
			if(str[i][j]=='R')	++mv[i];
			else if(str[i][j]=='*')	v[i][mv[i]]^=1;
			else if(str[i][j]=='0')	v[i][mv[i]]=2;
			else	v[i][mv[i]]=3;
		}
		uid[mv[i]].push_back(i);
	}
	US.flip();
	int ans=0,siz=0;
	for(int r=n;r;--r)
	{
		siz+=int(uid[n-r].size());
		for(auto id:uid[n-r])	for(int i=0;i<=n+2;++i)	Cv[i][v[id][i]][id]=1;
		memset(dp,0,sizeof dp);
		dp[0][0][0]=1;
		for(int p=1;p<=r;++p)
		{
			int Tup=1<<min(p-1,n-r+1),Sup=1<<min(p,n-r+1),U=Sup-1;
			for(int c=0;c<=1;++c)
			{
				for(int S=0;S<Tup;++S)
				{
					int cn=(c || ((S<<1)>U));
					if(p^r)	add(dp[p][cn][(S<<1)&U],dp[p-1][c][S]); // r 被强制选。
					sub(dp[p][cn][((S<<1)|1)&U],dp[p-1][c][S]);
				}
			}
			for(int c=0;c<=1;++c)
			{
				int ct=c || (p^r);
				for(int j=0;j<=min(n-r+1,p)-1;++j)	for(int t=ct;t<=3;++t)	F[(1<<j)][t]=Cv[j][t];
				for(int S=0;S<Sup;++S)
				{
					if(S)	for(int t=ct;t<=3;++t)	F[S][t]=F[S^lowbit(S)][t]|F[lowbit(S)][t];
					if(ct)
					{
						BT v1=F[S][1]|(F[S][2]&F[S][3]),v2=(F[S][2]|F[S][3])&(US^v1); // 这个时候 F[S][0] = U.
						int p1=v1.count(),p2=v2.count();
						mul(dp[p][c][S],Mul(pw2[p2],pw3[siz-p1-p2]));
					}
					else
					{
						BT v1=(F[S][0]&F[S][1])|(F[S][2]&F[S][3]),v2=(F[S][0]|F[S][1])&(F[S][2]|F[S][3])&(US^v1);
						int p1=v1.count(),p2=v2.count();
						mul(dp[p][c][S],Mul(pw2[p2],pw3[siz-p1-p2]));
					}
				}
			}
		}
		int dis=min(n-r+1,r),Sup=1<<dis;
		for(int c=0;c<=1;++c)
		{
			for(int s=1;s<=n-r;++s)
			{
				for(int j=0;j<dis;++j)	for(int t=c;t<=3;++t)	F[(1<<j)][t]=Cv[j+s][t];
				for(int S=0;S<Sup;++S)
				{
					if(S)	for(int t=c;t<=3;++t)	F[S][t]=F[S^lowbit(S)][t]|F[lowbit(S)][t];
					if(c)
					{
						BT v1=F[S][1]|(F[S][2]&F[S][3]),v2=(F[S][2]|F[S][3])&(US^v1);
						/*
						 这个时候 F[S][0] = U.
						*/
						int p1=v1.count(),p2=v2.count();
						mul(dp[r][c][S],Mul(pw2[p2],pw3[siz-p1-p2]));
					}
					else
					{
						BT v1=(F[S][0]&F[S][1])|(F[S][2]&F[S][3]),v2=(F[S][0]|F[S][1])&(F[S][2]|F[S][3])&(US^v1);
						int p1=v1.count(),p2=v2.count();
						mul(dp[r][c][S],Mul(pw2[p2],pw3[siz-p1-p2]));
					}
				}
			}
		}
		for(int c=0;c<=1;++c)	for(int S=0;S<Sup;++S)	sub(ans,dp[r][c][S]);
	}
	printf("%d",ans);
	return 0;
}
posted @   SyadouHayami  阅读(95)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构

My Castle Town.

点击右上角即可分享
微信分享提示