Logo __vector__ 的博客

博客

标签
暂无

ABC276E 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-06 22:57:52

题外话

赛时很快就想出了一个自认为错的算法,还随便整了个 hack 数据,然后去看 F 题,G 题。过了半个小时回来看,分析一下发现是对的。不过还是赶在比赛结束前 20min 切了。

做法

搜索。
没错,就是搜索。
当然,不是指数级的爆搜,是记忆化。 从起点开始搜,一旦找到任意一条从起点出发又回到起点的(长度大于 $0$ 的)路径,就退出;搜到一个已经访问到的点就立即返回。

听上去很不对,因为如果第一条路径没走好,还会挡住后面更长的路径搜不到。

但是,请注意,把图画出来之后,你会发现,只要找到任意一条从起点出发又回到起点的(长度大于 $0$ 的)路径,长度都符合要求,而显然只要存在从起点出发又回到起点的(长度大于 $0$ 的)路径,记忆化搜索肯定能找到其中一条。
举个极端的例子:

记忆化搜索找到了最短的一条从起点出发又回到起点的长度大于 $0$ 的路径,而这路径符合要求。

Code

#include <bits\/stdc++.h>
using namespace std;
const int maxn=1e6+5;
string str[maxn];
map<pair<int,int>,bool> vis;
int h,w;
int sth,stl;
int ans=0;
void dfs(int i,int j,int cnt)
{
	if(ans||i>=h||i<0||j>=w||j<0||str[i][j]=='#')return;
	if(!(i==sth&&j==stl)&&vis.find(make_pair(i,j))!=vis.end())return;
	vis[make_pair(i,j)]=1;
	if(i==sth&&j==stl&&cnt!=0)
	{
		if(cnt>=4)ans=1;
		return;
	}
	dfs(i+1,j,cnt+1);
	dfs(i-1,j,cnt+1);
	dfs(i,j-1,cnt+1);
	dfs(i,j+1,cnt+1);
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>h>>w;
	for(int i=0;i<h;i++)
	{
		cin>>str[i];
		for(int j=0;j<str[i].size();j++)
		{
			if(str[i][j]=='S')
			{
				sth=i,stl=j;
			}
		}
	}
	dfs(sth,stl,0);
	if(ans)printf("Yes");
	else printf("No");
	return 0;
}

撞标准库记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-11 21:27:29

前言

某蒟蒻看到某 dalao 于今年 CSP,随便起了个变量名,极端倒霉,撞了标准库,感到十分震撼,所以就用这个 blog 记录某蒟见过的撞标准库。

记录

  • 众所周知的
  1. next
  2. y1
  3. pipe
  • 我自己撞过的
  1. end
  2. equal
  3. data
  4. transform
  • (我见过的)别人撞过的
  1. bzero
  2. ffs
  3. index
  4. move
  5. hash
  6. j1
  7. j2

ABC277D 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-13 13:48:57

题外话

不知道为啥别人都用的并查集。
我赛时写的记忆化搜索过去的。

做法

把题目画出来,就是所有 $x$ 都可以互相到达,并且 $x$ 可以到达 $(x+1) \mod m$。

然后现在我们要让剩下的值最少,转化一下就是让选择的数字总值最大,其实就是找一条路径,使得路径权值和最大。

我们进行建边,首先所有相同数字都可以互相到达,到达了一个数,就可以走到其他所有相同的数。

所以基于尽可能多选的贪心,我们可以把相同的数缩成一个点。

缩完点之后,将每个数组中出现过的数 $x$ 连向 $(x+1) \mod m$。

然后依次枚举从 $a_i$ 出发,进行爆搜。

爆搜的时候把答案记一下就行了,就是记忆化。

复杂度 $O(n \log n)$。
那个 log 是 map 的。
用 unordered_map 可以 $O(n)$。

Code

#include <bits\/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
int n,m;
ll a[maxn];
map<ll,ll> cnt;
map<ll,bool> vis;
map<ll,ll> ans;
void dfs(ll num)
{
	if(cnt.find(num)==cnt.end())
	{
		return;
	}
	if(vis.find(num)!=vis.end())
	{
		return;
	}
	vis[num]=1;
	dfs((num+1)%m);
	ans[num]=ans[(num+1)%m]+cnt[num];
\/\/	printf("ans[%d]: %d\n",num,ans[num]);
}
int main()
{
	ll sum=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum+=a[i];
		cnt[a[i]]++;
	}
	sort(a+1,a+n+1);
	n=unique(a+1,a+n+1)-a-1;
	for(int i=1;i<=n;i++)
	{
		cnt[a[i]]=cnt[a[i]]*a[i];
	}
	for(int i=1;i<=n;i++)
	{
		if(vis.find(a[i])==vis.end())
		{
			dfs(a[i]);
		}
	}
	ll out=0;
	for(int i=1;i<=n;i++)
	{
		out=max(out,ans[a[i]]);
	}
	printf("%lld",sum-out);
	return 0;
}

ABC277E 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-13 14:24:25

题外话

此题我赛时没做出来,赛后被别人一句话搞懂。

做法

我们把题意略微转化一下:

给定两种类型的边,类型分别为 $1$,$0$。
你只能行走在你状态对应的边上。 例如你的状态是 $1$,就只能走在类型为 $1$ 的边上。
现在有一些节点作为开关。可以在那里切换状态。
问从 $1$ 走到 $n$ 的最短路。

看到有两种边,而且这两种边互不兼容。
而且有开关可以在两种边上切换。
现在瓶颈就在于如何选择是否切换状态。
那么是不是可以将这个“选择”变成一条权值为 $0$ 的边,来进行切换。
而现在有了代表是否选择切换的边,而原图上无法直接从一种边走到另一种边。
可以考虑把两种边分别建图。
而这个代表切换状态的边就是两个图之间的连边。

也就是分层图这个东西。

对种类为 $1$ 的边,所连节点编号为 $1$ 到 $n$。
对于种类为 $0$ 的边,所连节点编号为 $n+1$ 到 $2n$。
对于有开关的节点,设其为 $u$,则连接 $u$ 和 $u+n$。

节点 $n+1$ 到 $2n$ 可以认为是编号为 $1$ 到 $n$ 节点的另一种状态,只不过由于状态不同,这些节点无法通过普通的边,只能通过代表状态切换的边与编号为 $1$ 到 $n$ 互通。

建完图之后跑最短路就行了。

Code

#include <bits\/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int head[maxn*2];
struct EDGE
{
	int to,val,nxt;
}edge[maxn*4];
int cnt;
void add(int u,int to,int val)
{
	edge[++cnt].to=to;
	edge[cnt].val=val;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
int n,m,k;
bool vis[maxn*2];
int dis[maxn*2];
void dijkstra()
{
	memset(dis,0x3f3f3f3f,sizeof dis);
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> que;
	que.push(make_pair(0,1));
	dis[1]=0;
	while(!que.empty())
	{
		int u=que.top().second;
		que.pop();
		if(vis[u])continue;
		vis[u]=1;
\/\/		printf("u: %d\n",u);
		for(int i=head[u];i;i=edge[i].nxt)
		{
			int to=edge[i].to;
			if(dis[to]>dis[u]+edge[i].val)
			{
				dis[to]=dis[u]+edge[i].val;
			\/\/	printf("dis[%d]: %d dis[%d]: %d\n",u,dis[u],to,dis[to]);
				que.push(make_pair(dis[to],to));
			}
		}
	}
	int ans=min(dis[n],dis[n*2]);
	if(ans==0x3f3f3f3f)printf("-1");
	else printf("%d",ans);
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	int u,v,a;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&a);
		if(a)
		{
			add(u,v,1);
			add(v,u,1);
		}
		else
		{
			add(u+n,v+n,1);
			add(v+n,u+n,1);
		}
	}
	int si;
	for(int i=1;i<=k;i++)
	{
		scanf("%d",&si);
		add(si,si+n,0);
		add(si+n,si,0);
	}
	dijkstra();
	return 0;
}

NOIP2022 游记

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-16 23:27:39

CSP-S 取消了,虽然 vp 成绩过了 200,但还只是 vp 成绩,要翻转去年的 pj 2=,还得靠 NOIP。

Day -9

8:44 在二东校门口出发,去 pyyz。

车上看到 bct 又送了一场比赛,就把题下下来,看了看。T1 又是个期望。

我看着 zgc 和 ly 两位大佬一顿讨论之后把 T1 秒了,于是也开始做。

想了想,写了个 $O(n^2)$ 的 80 分 dp,结果样例一直过不去。

尝试修改状态仍然失败,被迫求助坐在旁边的 $\color{black}\text{z}\color{red}\text{gc}$,$\color{black}\text{z}\color{red}\text{gc}$ 看了一眼说,你这没取模。

吐血。

加上取模就过了样例。

然后去 T2 写了个 $O(n! \times n)$ 的暴力,就不管了。

然后颓了一会。

然后看着 zgc 颓 diep.io。

电脑没过多久就没电了,看了一会手机,然后在车上走来走去不知道干啥。

过了一会,到了 pyyz。

大概就是提着一大堆行李进了宿舍,此时是 15:30。

四个人的宿舍竟然只有两个插头。

于是我和 zgc 进行插座嵌套。

zgc (@cancan123456)曰:“这 NOI2022 选手有点逊啊,我去了能拿 Ag”。

然后就是收拾床,去搬床垫。

过了一会,去吃饭,不得不说特别好。

然后就是去机房。

去机房的时候正好 pyyz 好像是开始晚自习,路上特别热闹。

另外 pyyz 好大(顺便提一句,pyyz 围墙上有铁丝电网)

去了机房,看配置很不错,有 Vscode + sublime,Dev 也有 5.11 而不是去年 csp 的 5.1.0。

然后我们就知道了:我们现在坐的位置就是 NOIP 的座位。

这下可以无限试机了。

另外 $\color{black}\text{p}\color{red}\text{traffic114514}$ 还打了一堆板子,期望 noip 不会把之前文件删掉(

离开机房回宿舍,此时是 22:00,正好赶上 pyyz 放学,又是人山人海.........

听说宿舍有信号屏蔽器?!那我怎么打 CF!

先是 zgc 借了我热点,然而我热点寄了,于是我去借 pt 热点。

经过漫长的联网,总算连上了。

然而没啥时间了,很快就关灯了。

没法看书就去 oi-wiki 逛了一会,复习了 kmp 和 AC 自动机。

23:00 睡觉!

Day -8

早上吃完饭,就去参加模拟赛。
出题人 $\color{black}\text{H}\color{red}\text{6\_6Q}$

还是通读 4 题。

看 T1 是道水题,先水了,然后打了 3 个暴力。

回去对拍了 T1,拍出个错,改了改,然后就开始罚坐。

成绩出来了,T1 切了,T2 暴力写挂,T4 暴力出奇迹。

最终 100+0+20+30=150。
还不算太差。

然后就是晚上的 CF Div.3,又多亏了 pt 神仙借我热点。

然后我被 pt 飞快的手速,整的不敢提交了(orz pt)

然后 pt 赛时做出来 ABCD,赛后 D 被叉了。

Day -7

照例模拟赛,出题人 $\color{black}\text{q}\color{red}\text{azswedx2}$(唐椰叶)。

通读 4 题,发现一道不会()

没办法专攻 T1。

梳理一遍发现可以先枚举纵着切,然后 dp 求出横着切的最优值(实际上就是正解)。

然后设了设状态,发现没法高效转移。

又想了一会,还是没想法。

又想了想二分,我找到了一种贪心做法能高效求出在确定纵向切的方式,以及最小值的情况下,最多横着切多少刀。

于是我把贪心改了改,变成了一个已知纵向切,以及横着切多少刀的时候,计算最小值是否大于等于某个值,然后二分。

写完之后发现寄了,然后找出了一堆 bug,然后直接过了大样例,顺便提示了一下 pt。

最后把 T2 暴力写了。

得分 100+20+0+0=120。

晚上打了场 ABC,输麻了。

A 题数组开小 WA 了,甚至一时不知道怎么回事。

B,C 顺利 AC。

然而 D 题由于 sb 错,花了 20min 才过,旁边的 $\color{black}\text{p}\color{red}\text{traffic114514}$ 看到了,不屑的说:"你太逊了。“
PS: 我过 D 的时候,pt 巨神仙已经过了 E。

过了 D 就去看 E,E题很快想到了一个思路(实际上就是正解),然而我还当有问题,就没写,写了 $O(n^4)$,T了。

E 题也写了正解,结果没加剪枝,还把记录上一轮选的下标的部分写挂了。

AC 41,WA 3 TLE 4

最后寄了。

赛后 5 min 调出来。

然而涨 rating 了?!

Day -6

模拟赛寄了

晚上先是打了一场 ARC,旁边的 gyf(At & CF 号 $\color{black}\text{G}\color{red}\text{S128}$) 秒切 C,一度吊打 $\color{black}\text{t}\color{red}\text{ourist}$,成功被他带飞,然后 perf 过 2000,我的新号飞过灰和棕上了绿。

晚上 CF,打着打着断网了,成功废掉一个号。(我感谢宿舍的信号屏蔽仪)

Day -5

模拟赛寄掉一百多分,直接完蛋。

晚上打 Div.4,很快切到了 F。

结果 F 题刚准备测样例。

T M D 屏 蔽 仪 又 开 始 工 作 了。

寄!

而且这样就没机会检查了,交上去的 5 题能不能 Pass System test 还不知道。  

气得想砸电脑。算了颓了 5 min,睡觉了。(我答应了全宿舍,如果涨分了,就让他们机惨 1 h)

Day -4

早上起来,诶?我 rank 还比较正常,而且没有被叉。

去叉人,抓到某人 while(t--) 里面 memset,正要点 Hack it!,然后弹出来个 "You can't hack this submission"。

被人抢先一步了。

上午模拟赛完犊子了。

但是下午模拟赛加了几个减枝,暴力出奇迹,成功卡线拿到了衣服(貌似是前 20 都有)

Day -3

Rating 更新,CF 涨分了。

然而模拟赛又完犊子了。

Day -2

复习了一些板子(然而就是没复习割边,寄!)

Day -1

颓废。
去 lichess.org 下国际象棋。
还和 $\color{black}\text{p}\color{red}\text{traffic114514}$ 去 florr.io 打了几局 pvp,用一堆紫 Light 搞死了他的蓝 Misible 组合。
然后就是看纪录片放松。
晚上 zgc,gyf 还沉迷于 Matrix67 网站上的找规律游戏。

NOIP 当天

早上定了 6:00 的闹钟,然而起不来,就拖到了 6:30。

然后急急忙忙吃完了早饭,又得到消息得去做核酸。

做完核酸,收拾了一下东西,就去入场了。

然后发现我和 $\color{black}\text{c}\color{red}\text{ancan12345}$, $\color{black}\text{p}\color{red}\text{traffic114514}$ ,$\color{black}\text{M}\color{red}\text{r\_Eight}$ 在同一个考场。

进去之后还有 40min 左右,就是试机时间,我打了个 A+B 就开始摆了。

时间到!监考发了密码,然而不对。

监考去问,发现别的考场也都打不开试题。

我就等着,顺便试图暴力破解密码。

过了 10min 左右,等来了正确密码,解压之后,pdf 竟然还有一层密码。

不过这层密码就是之前发错的那个。

打开试题。

先开 T1,woc,题面怎么这么恶心,看到了个 对 998244353 取模 就有点慌。不过我把题面在草稿纸上画了出来之后,清晰了不少。

然后我根据 CSP-S2022 T1 的经验,迅速想到枚举左上端点,而其余点不需要暴力枚举,前缀和预处理一下方案数就 ok 了。

把大概步骤写在纸上,然后扔掉 T1,去看后面题。

现在是 9:00,已经草稿纸上切了 T1,时间充足。

看 T2,暴力不会,跳过。

看 T3,发现暴力有 15 分,再跳过,看 T4。

发现还是有 8 分暴力。

于是我先把 T4 8 分暴力打了(还好考前复习了 ST 表)。

顺利过了大样例 #1,大样例 #2 跑了 1min 还没跑完。

现在大概是 9:25。

然后去干 T3。

我得到了一个是个人都能想出来的结论:要判断所有点是否能互相到达,只需要从一个点出发看能不能访问所有的点。

然后我就写了一个一百多行的大爆搜。

写完之后 RE 了,赶紧检查,发现是忘写 return 了。

然后直接过了两个样例,自己造了暴力的极限数据,0.2s 跑过,就扔了。

看来了下时间,10:00 左右,还有 3 个小时 10 分钟去刚 T1 T2。

T2 写了个乱搞,但是由于绑测肯定没分。

好了,来干 T1!

虽然写起来很长,但是步骤已经记录在了纸上,所以还是比较顺利。

写完之后,把代码从头到尾盯了一遍,然后测样例。

T M D 样 例 一 的 第 二 个 答 案 错 了。

然后我加了一堆调试语句,发现预处理没写错,那只能是回答询问错了。

然后我盯了一会,发现我把处理第二种答案的部分调用的变量不小心写成第一个部分的变量了。

改了之后过了样例,我不放心,还输出中量,确定没错。

然后极度紧张地开始测大样例,看到

正在比较文件 plant.out plant2.ans
FC: 找不到差异  

正在比较文件 plant.out plant3.ans  
FC: 找不到差异  

好耶!

现在是 11:30 左右。

接着我就去尝试优化 T2,但无济于事,我总感觉这道题可能十分简单,但是死活推不出来。

T4 尝试第二个 subtask,但是根本找不出规律。

T3 打了个表,找到了一些规律,发现答案总是 $n$ 的倍数,但是没法用来做题。

最后半个小时,检查细节,主要是 T1。

然而没检查出什么错。

最后 1 分钟,NOIP rp++ NOIP rp++ NOIP rp++

很快比赛结束。

估分 100+0+15+8=123,感觉非常的寄。

出场的时候心态感觉比去年平稳了很多。

一出场,我就找 pt & dcy 问估分,pt T1 打了 60 分暴力,但是后面题拿了不少暴力分,dcy 切了 T1,但是后面题只有 T4 的 8 分。估分都在 100 左右。

然后就是回宿舍收拾(中间还走错了一次路),问了 zgc,gyf 的估分,zgc 估分 165,我表示膜拜,而 gyf 估分 110 左右。

gyf:“T1 不就是个大水题吗” 我:“是吗,我用了 40min 才写完代码”。

这利用了对比论证,把我的蒻和 gyf 的强进行了对比,凸显出了 gyf 的强。

集合之后,就是去酒店吃饭。

吃饭的时候忽然想起来 T1 没有检查模数,我一时变得特别紧张,怕一测全 WA。

坐大巴返程。

在车上得知 lhx (@cancan12345) T1 没看见多测,100 --> 1,在此默哀。

在车上颓了一会 florr.io,看 $\color{black}\text{Y}\color{red}\text{h0326}$ 打到 Wave 34。

我第一次成功合成红卡,还合了两个。然而 Yh0326 合青失败丢了四个红

过了一会,听说数据出来了,就尝试下载代码测试,结果网寄了,于是借 dcy(@快乐的大童) 的电脑测。

看了一眼,模数没打错,放心了许多。

话说 Waiting 了 2min 心脏病都要发了

T1 洛谷数据 AC 了,松了一口气。

后面题就不敢测了。

这时候忽然看见右边的 pt 神仙好像哭了,不知道是咋回事(事后看评测记录知道是他的 T1 暴力 WA 了),也不知说啥。

回去之后,用有道小图灵测了一下,发现挂了 8 分,得了 100+0+15+0 = 115 分。

小图灵预计奖项 1=

不过最终能不能拿一等,还不好说,毕竟如果 CCF T1 数据水,让暴力跑过,我就危了。

回来教练发了 SD 自己造的数据测试的全省成绩,100+0+15+8=123 ,在一等线以上。

过了两天小图灵更新,我居然擦线过了 ZJ 线,不过不可能是真的。

=========UPD==============
教练在群里发了官方数据下的全省成绩,没有 FST,123pts。

最终 SD 1= 线是 108,我省 rk87 成功拿到。

虽然一等了,但知道消息之后心情并没有什么变化,也只算是差不多拿到了 SDOI 的线下参与资格(去年 110 分就行,今年我觉得也高不到哪去)。

另外可喜可贺的是,我们 SDSC 集训的时候,我们宿舍本次 NOIP 全员 1=!本次 NOIP 考前集训,换宿舍之后也是全宿舍本次 NOIP 1=,同时 YTEZ 本次获得了 16 个一等奖,数量应该是 SD 最多了。

========Final==============

过 ZJ 线了

2022.12.13 出成绩了,正式拿到省一。

How to AK ARC145

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-07-30 22:19:35

题外话

历经 6 次罚时切掉 A 后,通过翻 OEIS.org 过 C(我很不要脸,对吧)。
反正小号是上棕了(76 --> 440)

A

做法

结论题。
考虑让右边试图匹配左边,根据题目条件,可以推出,对于所有不符合条件的位置,都可以通过牺牲后一个位置来把当前位置变得合格。

但是,最后一个位置没有位置可以替它牺牲,所以如果最后一个位置不符合条件,那么无解,No,反之,Yes

左边匹配右边和右边匹配左边都一样,所以选择较为方便的右边匹配左边。

Code

#include <bits\/stdc++.h>
using namespace std;
namespace Main
{
    const int maxn=2e5+5;
    int n;
    char s[maxn];
    void main()
    {
        scanf("%d",&n);
        scanf("%s",s+1);
        if(n==2)
        {
            if(s[1]!=s[2])printf("No");
            else printf("Yes");
            return;
        }
        if((s[n]=='B'&&s[1]=='A'))printf("No");
        else printf("Yes");
    }
}
int main()
{
    Main::main();
    return 0;
}

B

做法

正在想

Code

没写

C

公式是从 oeis.org 上贺的,我还没学证明

一些无聊的东西

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-07-31 19:52:56

我用 mt19937 随机生成了一个长度 $10^7$ 的字符串,只包含小写字母。

VScode 统计了各字符串出现次数。

大佬名字首字母:

  • pt 14797 次
  • zgc 546 次
  • lhx 606 次
  • yyx 587 次
  • dcy 577 次
  • ly 14864 次
  • tyy 525 次
  • qlr 606 次
  • wrf 545 次
  • zhq 562 次
  • wyz 542 次
  • qc 15051 次
  • chz 559 次
  • ljh 583 次
  • ymh 570 次
  • czy 564 次

常见字符串:

  • ytez 20 次
  • ssh 579 次
  • check 2 次
  • hacker 1 次
  • luogu: 2 次
  • oier 32 次
  • cspj 29 次
  • csps 24 次
  • noip 22 次
  • noi 598 次
  • akioi 1 次
  • china 1 次

关于原文件:这里

P2619 题解+带权二分记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-08-03 19:11:50

做法

首先忽略 $need$ 条白边的限制。
设 $f(x)$ 表示有 $i$ 条白边时的最小边权和。

由题解可知,这个函数是个凸函数。

对于这个函数,难以直接求出其中某个点的值,但是可以轻松求出它的最小值点。

所以,可以试着让 $need$ 点成为值最小的点。

现在已知值最小的点 $m$,尝试让它向 $need$ 点靠近。

如果 $m$ 点在 $need$ 点左边,那么将所有白边的权值加上 $w(w \ge 0)$,使得 $m$ 点向右移动(因为白边权值增加了,求最小生成树时,使用的白边数量减少,从而使得最小值点向左移动)。

反之亦然。

现在要做的,就是二分 $w$,使得 $need$ 点与 $m$ 点重合($need$ 点成为值最小的点)

二分出 $w$ 之后,还要把求得的最小边权和减去 $w \cdot need$,消去影响。

Code

\/*
Sto pt orz
Sto zgc orz
Sto lhx orz
Sto tyy orz
*\/
#include <bits\/stdc++.h>
using namespace std;
namespace Main
{
    typedef pair<int,int> PII;
    const int maxv=5e4+5;
    const int maxe=1e5+5;
    struct EDGE
    {
        int u,to,c,col;
        bool operator <(const EDGE& b)
        {
            return (c!=b.c)?(c < b.c):(col<b.col);
        }
    }edge[maxe];
    int cnt;
    inline void add(int s,int t,int c,int col)
    {
        edge[++cnt].to=t;
        edge[cnt].u=s;
        edge[cnt].c=c;
        edge[cnt].col=col;
    }
    int fa[maxv];
    int find(int x)
    {
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    int V,E,need;
    PII kruskal()
    {
        sort(edge+1,edge+E+1);
        int ans=0;
        int ecnt,whicnt;
        ecnt=whicnt=0;
        for(int i=1;i<=E;i++)
        {
            int a=edge[i].u,b=edge[i].to;
            if(find(a)!=find(b))
            {
                fa[find(a)]=find(b);
                ans+=edge[i].c;
                ecnt++;
                if(edge[i].col==0)whicnt++;
            }
            if(ecnt==V-1)break;
        }
        return make_pair(ans,whicnt);
    }
    void main()
    {
        scanf("%d%d%d",&V,&E,&need);
        int si,ti,ci,coli;
        for(int i=1;i<=E;i++)
        {
            scanf("%d%d%d%d",&si,&ti,&ci,&coli);
            add(si+1,ti+1,ci,coli);
        }
        int l=-111,r=111;
        int ans=0;
        while(l<=r)
        {
            int mid=l+r>>1;
            for(int i=1;i<=V;i++)fa[i]=i;
            for(int i=1;i<=E;i++)
            {
                if(edge[i].col==0)edge[i].c+=mid;
            }
            PII sto_pt_orz=kruskal();
            if(sto_pt_orz.second>=need)
            {
                ans=sto_pt_orz.first-need*mid;
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
            for(int i=1;i<=E;i++)
            {
                if(edge[i].col==0)edge[i].c-=mid;
            }
        }
        printf("%d",ans);
    }
}
int main()
{
    Main::main();
    return 0;
}

CF1714G 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-08-03 23:02:21

题外话:

感谢这道题把我送到了 1606th,并助我上了 pupil。

题意

定义 $r_i$ 如下:
从 $1$ 点到 $i$ 点路径上,最多前 $r_i$ 条边上的 $b$ 总和不大于 $1$ 到 $i$ 路径上每一条边的 $a$ 总和。

求 $r_2,r_3,\cdots,r_n$。

做法

既然需要用到总和,那么记录一下前缀和就行了。

显然,前缀和数组是递增的,也就是说,将对于每个点 $i$,在 $1$ 点到 $i$ 点路径上的前缀和数组二分,就能求出 $r_i$

至于怎么求,dfs 到一个点的时候,就顺便记录从 $1$ 点 到这个点路径上的 $a$ 前缀和,以及 $b$ 的前缀和,这个可以从父节点递推出来。

不过直接这么递推,记录的不是同一条路径上的前缀和

处理方法(或者说具体实现)是,维护一个 $top$,代表前缀和数组的顶部,当搜到一个点时,$top$ 加一,代表当前路径加上了一个点,同时记录前缀和;当退出当前点时,将 $top$ 减一,代表当前路径不再包含该点。

剩余的看代码注释吧。

Code

#include <bits\/stdc++.h>
using namespace std;
namespace Main
{
    typedef long long ll;
    const int maxn=2e5+5;
    int t;
    int head[maxn];
    struct EDGE
    {
        int to,nxt,a,b;
    }edge[maxn<<1];
    int cnt;
    inline void add(int u,int to,int a,int b)
    {
        edge[++cnt].to=to;
        edge[cnt].a=a;
        edge[cnt].b=b;
        edge[cnt].nxt=head[u];
        head[u]=cnt;
    }
    int n;
    ll qzh[maxn];
    int top;
    ll qzh2[maxn];
    int r[maxn];
    void dfs(int u,int giva,int givb,int _fa)
    {
        top++;\/\/当前路径的前缀和数组增加一个元素
        qzh[top]=qzh[top-1]+(ll)giva;\/\/记录 1---->u 的 a 前缀和
        qzh2[top]=qzh2[top-1]+(ll)givb;\/\/记录 1----->u 的 b 前缀和
        int pos=upper_bound(qzh2+1,qzh2+top+1,qzh[top])-qzh2;
        \/\/二分求出第一个 b 前缀和大于 1---->u 的 a 前缀和的点(第一个不符合要求的点)
        \/\/这个点在路径上的编号减去 1 就是最后一个符合要求的点。
        if(pos==top+1)
        {\/\/如果所有的点都符合要求,那就输出这条路径点的总量-1,就是 1----> u 的边的数量
            r[u]=top-1;
        }
        if(pos!=top+1)
        {\/\/如果不是所有的点都符合要求
            r[u]=pos-2;\/\/pos-1是最后一个符合要求的点,而 1---->(pos-1),有 pos-2 条边。
        }
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int to=edge[i].to;
            if(to==_fa)continue;
            dfs(to,edge[i].a,edge[i].b,u);
        }
        top--;\/\/退出当前点,当前路径不再包含该点
    }
    void main()
    {
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            for(int i=2;i<=n;i++)
            {
                int p,a,b;
                scanf("%d%d%d",&p,&a,&b);
                add(i,p,a,b);
                add(p,i,a,b);
            }
            dfs(1,0,0,0);
            for(int i=2;i<=n;i++)
            {
                printf("%d ",r[i]);
            }
            printf("\n");
            \/\/清空数据
            cnt=0;
            for(int i=0;i<=n;i++)
            {
                head[i]=0;
            }
        }
    }
}
int main()
{
    Main::main();
    return 0;
}

ABC262D 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-08-06 18:35:27

由于知道要讲题的时候,时间不多了,所以题解做的快一些,希望没有问题吧。

题意

给定一个元素个数为 $N$ 的集合,你可以从中选出任意多个元素(但至少选一个)。
问有多少种方式使得选出来的元素总和除以选出来的元素总个数是个整数。

做法

先列一个看完题就能想到的状态:
设 $dp_{i,j,k}$ 代表前 $i-1$ 个数,选了 $j$ 个,总和为 $k$ 的方案数。
dp 主体运行完成之后,对于所有总和为 $k$ 的倍数的方案统计答案。

这个式子超时是一定的。

这个东西,之所以慢,是因为考虑了很多不需要考虑的东西,如总和。

可以发现,对最后有影响的,仅仅是总和取余总数量为 $0$ 的结果。

也就是说,对于统计总和的地方,实际上只需要考虑余数就行了。

新的状态:
设 $dp_{i,j,k}$ 代表前 $i-1$ 个数,当前选了 $j$ 个,总和取余总个数为 $k$ 的方案数。

然后转移就行了。

转喻的过程:
第一层枚举一共要选多少个,记为 $num$。
里面枚举 $i,j,k$,进行转移,根据状态定义。
方程:
dp[i+1][j][k]+=dp[i][j][k] 代表这一位不选。

dp[i+1][j+1][(k+a[i])%num]+=dp[i][j][k] 代表选这一位。

第一层循环每枚举一个,就将当前的答案加上,最后输出答案总和。

题外话:

dp[i] 表示 前 i-1 位答案的很奇怪,对吧,那是因为我的设 dp[i] 表示前 i 位的答案的代码因为离奇原因寄了,所以我只写了 dp[i] 表示前 i-1 位答案版本的。

Code

#include <bits\/stdc++.h>
using namespace std;
namespace Main
{
    typedef long long ll;
    const ll mod=998244353;
    const int maxn=105;
    ll dp[maxn][maxn][maxn];
    \/\/前 i 位选 j 个
    int a[maxn];
    int n;
    void main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        ll ans=0;
        for(int num=1;num<=n;num++)\/\/一共选多少个
        {
            memset(dp,0,sizeof dp);
            dp[1][0][0]=1;
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<=num;j++)
                {
                    for(int k=0;k<=num;k++)
                    {
                        dp[i+1][j][k]+=dp[i][j][k];
                        dp[i+1][j][k]%=mod;
                        dp[i+1][j+1][(k+a[i])%num]+=dp[i][j][k];
                        dp[i+1][j+1][(k+a[i])%num]%=mod;
                    }
                }
            }
            ans+=dp[n+1][num][0];
            ans%=mod;
        }
        printf("%lld",ans);
    }
}
int main()
{
    Main::main();
    return 0;
}
共 320 篇博客