首页 > 其他分享 >YsOI2023 小记

YsOI2023 小记

时间:2023-08-15 09:59:07浏览次数:34  
标签:typedef YsOI2023 int res ll long read 小记

D2T1

签。

#include <cstdio>
using namespace std;
int read(){/*...*/}
typedef long long ll;
void solve(){
	ll n=read()-1,x=read();
	ll y=x;
	while(~y&1) y>>=1;
	for(int i=1;i<n;++i) y<<=1;
	if(x>y) y=x;
	printf("%lld\n",y);
}
int main(){
	int tc=read();
	while(tc--) solve();
	return 0;
}

D2T2

诈骗。发现翻转一个区间一定不会增加新的灵异区间,直接统计初始值即可。

#include <cstdio>
#include <unordered_map>
using namespace std;
int read(){/*...*/}
typedef long long ll;
const int N=100003;
int n,a[N];
int main(){
	n=read();
	unordered_map<int,int> mp;
	long long res=0;
	++mp[0];
	for(int i=1;i<=n;++i)
		res+=mp[a[i]=read()^a[i-1]]++;
	printf("%lld\n",res);
	return 0;
}

D1T1

首先由于 bfs 树分层,我们只需要对于跨层边考虑,发现限制相当于对于连着同一个节点一条树边和一条非树边,求出这两条边的另一个端点的 LCA,那么对于这个 LCA 分别往这两个端点延伸的树边有一个偏序的限制,拓扑排序即可。注意重边。

#include <cstdio>
#include <vector>
using namespace std;
int read(){/*...*/}
typedef long long ll;
const int N=200003,M=400003;
vector<int> vec[N];
int n,m;
int eu[M],ev[M];
namespace D{
	const int N=::M;
	const int M=1000003;
	int hd[N],ver[M],nxt[M],tot;
	int deg[N];
	void add(int u,int v){
		++deg[v];nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;
	}
	int que[N],tl;
	void topo(){
		for(int i=1;i<=m;++i) if(!deg[i]) que[++tl]=i;
		for(int pos=1;pos<=tl;++pos){
			int u=que[pos];
			printf("%d %d\n",eu[u],ev[u]);
			for(int i=hd[u];i;i=nxt[i]){
				int v=ver[i];
				if(!--deg[v]) que[++tl]=v;
			}
		}
	}
}
namespace G{
	int hd[N],ver[M<<1],nxt[M<<1],tot=1;
	void add(int u,int v){
		nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;
	}
}
namespace T{
	int hd[N],ver[N<<1],nxt[N<<1],tot;
	void add(int u,int v){
		nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;
	}
	int de[N];
	void dfs(int u){
		vec[de[u]].emplace_back(u);
		for(int i=hd[u];i;i=nxt[i]){
			int v=ver[i];
			de[v]=de[u]+1;
			dfs(v);
		}
	}
}
using T::de;
int p[N],bel[N],f[18][N];
int main(){
	n=read();m=read();
	for(int i=1;i<=m;++i){
		int u=eu[i]=read();
		int v=ev[i]=read();
		G::add(u,v);G::add(v,u);
	}
	int rt=0;
	for(int i=1;i<=n;++i){
		f[0][i]=p[i]=read();
		if(!p[i]) rt=i;
		else T::add(p[i],i);
	}
	for(int t=1;t<18;++t)
		for(int i=1;i<=n;++i) f[t][i]=f[t-1][f[t-1][i]];
	T::dfs(rt);
	for(int d=1;d<=n;++d)
		for(int x:vec[d]){
			for(int i=G::hd[x];i;i=G::nxt[i]){
				int v=G::ver[i];
				if(v==p[x]) bel[x]=i>>1;
			}
			for(int i=G::hd[x];i;i=G::nxt[i]){
				int v=G::ver[i];
				if((i>>1)==bel[x]) continue;
				if(de[v]==d-1){
					int u=p[x];
					for(int t=17;~t;--t)
						if(f[t][u]!=f[t][v]){u=f[t][u];v=f[t][v];}
					if(u!=v) D::add(bel[u],bel[v]);
					else D::add(bel[x],i>>1);
				}
			}
		}
	D::topo();
	return 0;
}

D1T2

\(m=n-1\) 为 Prufer 序板子,\(m=n\) 考虑建出仙人掌圆方树,由于只有一个方点相当于做 \(n+1\) 个点的树的情况。

\(m=n+1\) 依旧考虑建圆方树,对于有两个非平凡点双的情况相当于做 \(n+2\) 个点的树,但是要容斥去两个方点相邻的情况。

对于只有一个点双,发现点双形态一定是杏仁,简单计算即可。

#include <cstdio>
using namespace std;
int read(){/*...*/}
typedef long long ll;
const int N=100003,P=998244353;
int fac[N],fiv[N];
int qp(int a,int b=P-2){/*...*/}
int n,m;
int a[N];
namespace tree{
	void solve(){
		int sum=0;
		int res=fac[n-2];
		for(int i=1;i<=n;++i){
			res=(ll)res*fiv[a[i]-1]%P;
			sum+=a[i]-1;
		}
		if(sum!=n-2) puts("0");
		else printf("%d\n",res);
	}
}
namespace cir{
	void solve(){
		int sum=0;
		int res=fac[n-1];
		for(int i=1;i<=n;++i){
			res=(ll)res*fiv[a[i]-1]%P;
			sum+=a[i]-1;
		}
		if(sum>n-2) puts("0");
		else{
			if(res&1) res+=P;
			res>>=1;
			printf("%d\n",res);
		}
	}
}
int C(int n,int k){return (ll)fac[n]*fiv[k]%P*fiv[n-k]%P;}
namespace sol{
	int ss;
	int calc(){
		int res=fac[n-1],sum=0;
		for(int i=1;i<=n;++i){
			res=(ll)res*fiv[a[i]-1]%P;
			sum+=a[i]-1;
		}
		if(sum>n-1) return 0;
		res=(ll)res*fiv[n-1-sum]%P;
		ss=sum;
		return res;
	}
	int tmp;
	int sub1(){
		int k=n-ss;
		if(k<=3) return 0;
		int coe=((ll)k*(k-1)>>1)%P;
		coe=(ll)coe*(C(k,2)+P-3)%P*fac[k-2]%P;
		coe=(ll)coe*qp(6)%P;
		coe=(ll)coe*tmp%P;
		return coe;
	}
	const int ivs=qp(4);
	int sub2(){
		int res=fac[n],sum=0;
		for(int i=1;i<=n;++i){
			res=(ll)res*fiv[a[i]-1]%P;
			sum+=a[i]-1;
		}
		if(sum>n) return 0;
		int cur=0;
		for(int i=0;i<=n-sum;++i){
			int coe=(ll)res*fiv[i]%P*fiv[n-sum-i]%P;
			coe=(coe-(ll)tmp*C(n-sum,i))%P;
			if(coe<0) coe+=P;
			if(i>1&&n-sum-i>1)
				cur=(cur+(ll)fac[i]*fac[n-sum-i]%P*coe%P*ivs)%P;
		}
		if(cur&1) cur+=P;
		return cur>>1;
	}
	void solve(){
		tmp=calc();
		int res=sub1()+sub2();
		if(res>=P) res-=P;
		printf("%d\n",res);
	}
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;++i) a[i]=read();
	fac[0]=1;
	for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%P;
	fiv[n]=qp(fac[n]);
	for(int i=n;i;--i) fiv[i-1]=(ll)fiv[i]*i%P;
	if(m==n-1){
		tree::solve();
		return 0;
	}
	if(m==n){
		cir::solve();
		return 0;
	}
	sol::solve();
	return 0;
}

D1T3

考虑如何形式化 Prufer 序的过程,相当于维护一个当前的叶子点集 \(S\) 和已经删除的点集 \(T\),每次移除叶子点集中最小的点到删除点集,然后看当前点是否能加入叶子点集。

由于这道题 Prufer 序整体形态不确定,我们可以先枚举初始的叶子点集(相当于在 DP 数组初始化时将这些点置为 1),然后每次遇到序列中的一个点决策其是否加入子序列中/是否加入叶子点集。只要最后 \(|S|=1,|T|=n-2\) 那么就说明中途的 Prufer 序其实合法。

现在考虑统计距离期望。我们把 DP 倒过来,每次对于每个不在 \(T\) 中的点 \(x\) 记录所有可能距离总和及其方案数。相当于设 \(DP_{S,T,x}\) 如果当前最小的叶子就是 \(x\),那么将它连上 \(a_t\) 的父亲时它的值要从 \(DP_{*,*,a_t}\) 处继承。

考虑优化掉 \(3^n\)。注意到 \(S,T\) 实际上的对数不是很多,事实上,结合 Prufer 序的 \(O(n)\) 转化法,我们发现 \(S\) 大概是 \(S\cup T\) 的一段后缀,但除了一个新加的点例外。所以它的实际总数是 \(O(2^n n^2)\) 的。然而这还不足以通过。

我们发现当前 \(S\) 中最小的点很快就要被删掉了,我们其实并不关心它具体是多少,只用知道它是不是 \(x\)。所以只记录一个 \(0/1\) 状态,总复杂度 \(O(2^n n^2 m)\)。

由于鬼畜的数据范围全程 vector,但是注意不要每次将数组滚动清空,否则会 T。

#include <cstdio>
#include <array>
#include <vector>
#include <algorithm>
#define fi first
#define se second
typedef long long ll;
using namespace std;
int read(){/*...*/}
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef array<pii,2> ar;
typedef vector<ar> va;
typedef vector<va> vva;
typedef vector<vva> vvva;
const int P=1000000007;
void inc(int &x,int v){if((x+=v)>=P) x-=P;}
int n,m,_n,len;
int jump(int s,int x){
	++x;
	if(!(s>>x)) return _n;
	return __builtin_ctz(s>>x)+x;
}
int qp(int a,int b=P-2){/*...*/}
int main(){
	n=read();m=read();_n=n-1;len=(1<<_n);
	vi arr(m),res(_n);
	for(int i=0;i<m;++i) arr[m-i-1]=read()-1;
	vvva f(len,vva(n,va(n)));
	for(int i=0;i<n;++i){
		f[len-1][_n][i][0]=pii(0,1);
		f[len-1][_n][i][1]=pii(1,1);
	}
	vi fac(m+1),fiv(m+1);
	fac[0]=1;
	for(int i=1;i<=m;++i) fac[i]=(ll)fac[i-1]*i%P;
	fiv[m]=qp(fac[m]);
	for(int i=m;i;--i) fiv[i-1]=(ll)fiv[i]*i%P;
	for(int x=0;x<m;++x){
		for(int s=1;s<len;++s){
			if(s>>arr[x]&1) continue;
			for(int lim=0;lim<n;++lim)
				if(lim==_n||(s>>lim&1))
					for(int t=0;t<n;++t){
						if(arr[x]<_n){
							int _s=s|(1<<arr[x]);
							int _lim=jump(_s,lim);
							if(arr[x]<lim){
								inc(f[s][lim][t][0].fi,f[_s][lim][t][arr[x]==t].fi);
								inc(f[s][lim][t][0].se,f[_s][lim][t][arr[x]==t].se);
								inc(f[s][lim][t][1].fi,f[_s][lim][arr[x]][1].fi);
								inc(f[s][lim][t][1].se,f[_s][lim][arr[x]][1].se);
								inc(f[s][lim][t][1].fi,f[_s][lim][arr[x]][1].se);
							}
							else{
								inc(f[s][lim][t][0].fi,f[_s][_lim][t][lim==t].fi);
								inc(f[s][lim][t][0].se,f[_s][_lim][t][lim==t].se);
								inc(f[s][lim][t][1].fi,f[_s][_lim][arr[x]][lim==arr[x]].fi);
								inc(f[s][lim][t][1].se,f[_s][_lim][arr[x]][lim==arr[x]].se);
								inc(f[s][lim][t][1].fi,f[_s][_lim][arr[x]][lim==arr[x]].se);
							}
						}
						if(lim<_n){
							int _lim=jump(s,lim);
							inc(f[s][lim][t][0].fi,f[s][_lim][t][lim==t].fi);
							inc(f[s][lim][t][0].se,f[s][_lim][t][lim==t].se);
							inc(f[s][lim][t][1].fi,f[s][_lim][arr[x]][lim==arr[x]].fi);
							inc(f[s][lim][t][1].se,f[s][_lim][arr[x]][lim==arr[x]].se);
							inc(f[s][lim][t][1].fi,f[s][_lim][arr[x]][lim==arr[x]].se);
						}
					}
		}
	}
	int iv=(ll)fac[n-2]*fac[m-n+2]%P*fiv[m]%P;
	for(int s=1;s<len;++s){
		int lb=__builtin_ctz(s);
		int p=jump(s,lb);
		for(int i=0;i<_n;++i) inc(res[i],f[s][p][i][lb==i].fi);
	}
	for(int i=0;i<_n;++i) res[i]=(ll)res[i]*iv%P;
	for(int i=0;i<_n;++i) printf("%d ",res[i]);
	putchar('\n');
	return 0;
}

D1T4

好题。对于一个有限的正整数集 \(S\),\(F(S)=\{x-y|x,y\in S,x>y\}\) 有一个大小下界 \(|S|-1\),而这个大小下界仅在等差数列处取到。

回顾题目中的博弈过程,我们发现轮到某一个固定的人操作时两个人数集大小的差是一样的。而当正在操作的人的数集大小比另一个人至少大二,那么显然这个人永远有办法给对方插入一个数。此时由于游戏必然有限轮内结束这个人必胜。

设先手 \(n\) 个数,后手 \(m\) 个数,当 \(m<n-1\) 时先手必胜,\(m>n\) 时后手必胜。

注意到 \(|F(S)|\) 取到 \(|S|-1\) 的条件是很苛刻的,绝大多数情况下我们可以认为 \(m=n\) 后手胜,\(m=n-1\) 先手胜。

考虑何时出现“以少胜多”的反例。结束局面时输家有一个 \(t+1\) 项的等差数列 \(\{a,a+d,a+2d,\dots,a+td\}\),而赢家正好有 \(\{d,2d,\dots,td\}\)

标签:typedef,YsOI2023,int,res,ll,long,read,小记
From: https://www.cnblogs.com/yyyyxh/p/YsOI2023.html

相关文章

  • [YsOI2023] 广度优先遍历 逆向输出路径(分层建树拓扑序. LCA)
    今天的模板测试是无向图上的广度优先遍历,【数据删除】马上写好了代码:1#include<cstdio>2#include<cstring>3#include<iostream>4#include<algorithm>5#include<vector>6#include<queue>7usingnamespacestd;8constintmaxn=100005;......
  • 【题解】洛谷 P9532 [YsOI2023] 前缀和
    原题链接【LGR-151-Div.2】洛谷8月月赛II&YsOI2023T1解题思路设有一序列a,其中a1 =a2,第k(≥3) 项为前k-1项的前缀和。可以发现前q项分别为第一项的20 倍,20 倍,21 倍,22 倍,23 倍…2q-3 倍,2q-2 倍。扩展到整个序列中,可得第i( ≥ 3)项一定为2i-2a1。......
  • P9534 [YsOI2023] 广度优先遍历
    好题。首先考虑到对于任意的边的输入顺序,分层图是不会变的,即所有点到根的最短距离不变。那么分为两种边,分别为不同层的边相连,相同层的边相连。显然第二种边是无用的,我们将其放到最后输出即可。由于下层的决策会影响上层的决策而且不同层之间的边的顺序不会影响答案,所以我们按......
  • 定点补码乘法器小记
    目录硬件模拟软件无脑乘Booth乘法器华莱士树优化的华莱士树参考链接:《计算机体系结构基础第三版》定点补码乘法器一生一芯学习讲义一生一芯视频号硬件模拟软件软件方式即类似我们手工计算,如计算1101*0101+00001101(乘数最低位1,结果加上被乘数。将乘数右移,被乘数左移)+0......
  • 「Log」2023.8.11 小记
    间幕\(1\)从今天开始记小记。七点到校了,先小摆一会,然后整理博客。听MITiS的电音,开始写题。\(\color{blueviolet}{P1829\[国家集训队]\Crash的数字表格\/\JZPTAB}\)莫反练习题,式子并不难推,两个整除分块解决。八点整打完,开始调。忘记初始化了。筛质数pri[++pcnt]=tr......
  • spring boot自定义类中 @Autowired注入失败问题小记
    springboot自定义类中@Autowired注入失败问题小记第一种方法:@PostConstruct,大多数人使用的方式,不过对于我的问题没有用第二种方法:实现ApplicationRunner接口,在run方法执行后进行初始化第三种方法:实现ApplicationContextAware接口,直接到spring容器拿bean代码如下shiroConf......
  • 多项式小记
    先粘个\(\rmNTT\)和\(\rmFFT\)的板子。inlinevoidtimes(LL*f,LL*g,intn,intlim){ intkn=initr(n);NTT(f,kn,1);NTT(g,kn,1); for(inti=0;i<kn;i++)f[i]=f[i]*g[i]%Mod; NTT(f,kn,-1);NTT(g,kn,-1); clr(f,lim,kn);}分治\(NTT/FFT\)大概就是维护下面......
  • utools插件生活小记今日提交发布「审核中,预计下周二通过」
     简介生活小记是一款集日常记事,待办,小工具等功能于一身,努力打造小而美的笔记类插件,希望大家会喜欢!功能:生活记事,可直接粘贴图片(小于2mb),列表可以导出笔记为html文件简单记录待办事项小工具:目前上了时间间隔、时间推算两个规划1、后续会完善现有功能,达到好用、易用2、......
  • Mssql手工注入执行命令小记
    文章写于2021-04-08,首发于https://www.anquanke.com/post/id/237031#h2-6前言本次渗透通过某处SQL注入点进行源码分析,并手工利用xp_cmdshell进行了命令执行。初现在某个晴朗夏日午后,闲来无事想测试,这不,马上就掏出xray扫描到了一个sql注入漏洞,不得不说xray真的挺好用的。该项目......
  • python爬虫学习小记——lxml板块
    python爬虫学习小记——lxml板块lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言,它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索。XPath的选择功能......