首页 > 其他分享 >noip模拟1

noip模拟1

时间:2024-10-30 20:09:59浏览次数:1  
标签:noip int sum cin long dep top 模拟

A 追逐游戏 (chase)

后悔没做玮给的那道题了。。

就是分讨,有两种情况。

第一种是那个人先到终点,追的人追不上,这样答案就是追的人到终点的距离,在终点追上。

第二种是追的人先被追的人的毕经之路上(当然也可以一开始就在必经之路上),这样等效于一条从 \(x->s\) 的树链,被追的人走 \(\frac{x+s+1}{2}\) 步。

那这个就是类似于树上走 \(k\) 步的问题了,和上面那道题完全一样。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,q;
const int N=1e6+5;
vector<int>e[N];
int dep[N],siz[N],son[N],id[N],fa[N],rk[N];
void dfs1(int u,int f)
{
//	cout<<u<<" ";
	fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
	for(int v:e[u])
	{
		if(v==f) continue;
		dfs1(v,u), siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
int tim,top[N];
void dfs2(int u,int t)
{
	top[u]=t,id[u]=++tim,rk[tim]=u;
	if(son[u]) dfs2(son[u],t);
	for(int v:e[u])
	{
		if(v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}
int lca(int u,int v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		u=fa[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	return u;
}
inline int dis(int u,int v)
{
	return dep[u]+dep[v]-2*dep[lca(u,v)];
}
bool tag[N];
int jmp(int x,int k)
{
	while(114)
	{
		if(dep[x]-dep[top[x]]+1>k) break;
		k-=dep[x]-dep[top[x]]+1;
		x=fa[top[x]];
	}return rk[id[x]-k];
}
int s;
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>s>>q;
	for(int i=1;i<n;i++)
	{
		int u,v;cin>>u>>v;
		e[u].push_back(v),e[v].push_back(u);
	}
	dfs1(1,0),dfs2(1,1);
	while(q--)
	{
		int d,x;
		cin>>x>>d;
		if(dis(s,x)<d)
		{
			s=x;
			cout<<s<<" ";continue;
		}
		int lc=lca(x,s);
		int dis1=dis(s,lc),dis2=dis(x,lc);
		if(dis1>d) s=jmp(s,d);
		else s=jmp(x,dis2-(d-dis1));
		cout<<s<<" ";
	}
}

B 统计

这个题很神。考虑一个区间是符合要求的,可以简单地想到区间内元素的和是 \(\displaystyle\sum_{i=1}^m i\) 的倍数。

但是由于哈希冲突的类似问题,会有些许非法序列也符合这样的条件,那解决方法很简单。

考虑像 \(Treap\) 一样引入一个东西叫做 \(val\),对于值域内的所有数,我们给他 \(rand\) 一个 \(val\) 作为他的映射。

这样就能有效避免非法多算的情况。

我们得出的复杂度是 \(O(n^2)\),有 \(40\) 分。

进一步地,我们进行参照的和是 \(\displaystyle\sum_{i=1}^m val_i\),每次对于一个序列的前缀和去判断是否能被它整除,也就是:

\[ans=\sum_{i=1}^n \sum_{j=i+1}^{n} [(S_j-S_i) \mod \sum_{k=1}^m val_k==0] \]

如果我们能让 \(\displaystyle\sum_{k=1}^mm val_k=0\),那我们的判断条件就成了 \((S_j-S_i)=0\) 也就是 \(S_j=S_i\) 了。

这样只需要统计出有多少个前缀和相等即可,每一个相等的前缀和的功效都是 \(C_n^2\) 的。

那么最简单的方法,我们令 \(\displaystyle val_m=- \sum_{i=1}^{m-1}\) 即可解决。

时间复杂度 \(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rnd rand
int m,T,n;
const int N=1e6+6;
int a[N];
long long val[N];
long long sum[N];
signed main()
{
	freopen("st.in","r",stdin);
	freopen("st.out","w",stdout);
	mt19937 rnd(time(0));
//	srand(time(NULL));
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		int ans=0;
		for(int i=1;i<=n;i++) cin>>a[i];
		int s=0;
		for(int i=1;i<m;i++) val[i]=rand(),s+=val[i];
		val[m]=-s;
		for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+val[a[i]]);
		sort(sum,sum+1+n);
		int cnt=1;
		for(int i=1;i<=n;i++)
		{
			if(sum[i]!=sum[i-1]) cnt=0;ans+=cnt;cnt++;
		}
		cout<<ans<<"\n";
	}
	return 0;
}

C 软件工程

额,贪心即可。

考虑每个线段的分布很离♂ 散,那么我们就可以贪心地保留尽可能多的单个线段,因为单个线段的交是它本身。

并且,两个线段的交一定小于等于其中后任意一个线段。

那么,我们找前 \(k-1\) 个最大的线段,让它们单独成一个集合,剩下一个集合把其他线段扔进去,能拿到近乎满分。

若要满分,第一个点跑个暴力深搜就好了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 5005
#define M 14
using namespace std;
int n,k;
struct Seg{
	int l,r;
	bool operator<(const Seg &x)const{
		return r-l+1>x.r-x.l+1;
	}
}Sg[N];
int ans;
const int inf=1e18;
int p[N];
vector<int>t[N];
int x[N],y[N];
void dfs(int stp)
{
	if(stp==n+1)
	{
		int res=0;
		for(int i=1;i<=k;i++)t[i].clear();
		for(int i=1;i<=n;i++) t[p[i]].push_back(i);
		for(int i=1;i<=k;i++)
		{
			if(!t[i].size())continue;
			int tmp=t[i][0];
			int jx=x[tmp],jy=y[tmp];
			for(int j:t[i])
			{
				if(j==t[i][0]) continue;
				if(jx<x[j])
				{
					if(jy<x[j]) 
					{
						jx=jy=0;break;
					}
					else jx=x[j],jy=min(jy,y[j]);
				}
				else
				{
					if(jx>y[j])
					{
						jx=jy=0;break;
					}
					else jy=min(jy,y[j]);
				}
			}
			res+=max(0ll,jy-jx);
		}
		ans=max(ans,res);
		return ;
	}
	for(int i=1;i<=k;i++)
	{
		p[stp]=i;
		dfs(stp+1);
	}
}
signed main()
{
	freopen("se.in","r",stdin);
	freopen("se.out","w",stdout);
	scanf("%lld%lld",&n,&k);
	if(n<=10)
	{
		for(int i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]);
		dfs(1);
		cout<<ans;
		return 0;
	}
	for(int i=1,l,r;i<=n;i++)
	{
		scanf("%lld%lld",&l,&r);
		Sg[i]=(Seg){l,r};
	}
	sort(Sg+1,Sg+1+n);
	for(int i=1;i<k;i++)ans+=Sg[i].r-Sg[i].l;
	int l=-inf,r=inf;
	for(int i=k;i<=n;i++)
	{
		l=max(l,Sg[i].l);
		r=min(r,Sg[i].r);
	}
	ans+=l>r?0:r-l;
	printf("%lld",ans);
	return 0;
}

D 命运的X

脑电波题,不太可想象。

最终答案是 \(m^n+\sum_{p\in nxt}m^p\),其中 \(nxt\) 是 \(B\) 数组的 Border 集合。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int ppow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod,b>>=1;
	}return res;
 }
const int N=2e5+5;
int n,m,a[N];
int nxt[N];
signed main()
{
	
	freopen("x.in","r",stdin);
	freopen("x.out","w",stdout);
	int T;cin>>T;
	while(T--)
	{
		cin>>m>>n;
		for(int i=0;i<n;i++) cin>>a[i];
		memset(nxt,0,sizeof(nxt));
		int j=0;
		nxt[1]=0;
		for(int i=2;i<=n;i++)
		{
			while(j&&a[i]!=a[j+1]) j=nxt[j];
			if(a[j+1]==a[i])j++;
			nxt[i]=j;
		}
		int ans=ppow(m,n);
		int now=nxt[n];
		while(now) ans=(ans+ppow(m,now))%mod,now=nxt[now];
		cout<<ans%mod<<"\n";
	}
}

标签:noip,int,sum,cin,long,dep,top,模拟
From: https://www.cnblogs.com/ccjjxx/p/18516513

相关文章

  • 模拟赛总结(四)(终章?)
    2024.10.30T1追逐游戏(chase)被自己的分讨绕死了,以后要学会简化codeT2统计codeT3软件工程选前\(k-1\)长的+剩下求交集可得\(96\)~~为什么我贪的不对qwq~~把这个贪心改成大炮就是整洁的一部分定义\(dp_{i,j}\)表示前\(i\)条线段放到\(j\)个集合里,那么上述方法......
  • CSP 模拟 54
    赛前最后一场,也是最烂的一场。T1Alice和璀璨花看着像LIS,但是不知道应不应该去取最长的,不妨证明一下,对于当前位置,他一定比上一个位置大,如果不去取之前的最长的,那么需要的新代价会更大,所以直接取最长的即可,赛时T2Bob与幸运日不会,赛时以为是小清新同余题,结果他不清新,被硬控......
  • CSP 模拟 53
    T1冒泡排序(bubble)手玩一下发现就是对每个同余类排序。T2染色(color)先考虑不删,发现颜色的相对位置不会变,缩成没有连续段之后的长度为\(len\),相当于选出\(len\)个正整数,使得他们的和是\(n\),方案数为\(n-1\chooselen-1\),赛时到这里就一直在想DP,但是不会合并,其实这个东西就......
  • NOIP 模拟 1
    A追逐游戏(chase)答案具有单调性,直接求\(k\)级祖先和距离即可,倍增会被卡,上树剖轻松跑,时间复杂度\(\mathcal{O}(n\log^2n)\),上长剖可以少个\(\log\),题解是分讨到达点,感觉比较一般。B统计直接给每个数随机赋值来哈希,检查是否是和的倍数即可,不过哈希范围要大一些,不然容易冲......
  • 10.30 模拟赛
    复盘T1。好像很好做。先想了一个\(\mathcalO(n|c_{i,j}|^2)\)但是带四倍常数的做法。感觉加上一些优化和卡常后问题不大。于是开写。代码好长!!!调试好久!!!调完后样例6跑20s,最终优化后还是7s。实在优化不了了于是考虑换做法。发现枚举三条边后,剩下的用类似扫描线边扫边用树......
  • P1002 [NOIP2002 普及组] 过河卒
    棋盘上�A点有一个过河卒,需要走到目标�B点。卒行走的规则:可以向下、或者向右。同时在棋盘上�C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。棋盘用坐标表示,�A点(0,0)(0,0)、�B点(�,�)(n,m),同样马的位置......
  • NOIP2024 模拟赛19
    A拆位算贡献,枚举每一个位置,与操作两者都是\(1\),异或操作相反,或操作有一个是\(1\)即可。B观察到条件\(a_1\lek\)证明是必然有答案的,答案这样构成:从\(1\)走到任意点\(j\),然后\(j\)挖空,然后推到\(i\),记\(f_i\)为从\(1\)走到\(i\)的最小花费,答案\(i\)即为\(f_......
  • 【并查集】【中间值范围】NOIP2017]奶酪
    https://ac.nowcoder.com/acm/contest/22904/1027开了ll还见祖宗注意x^2+y2算完之后先判断有没有超4r2的范围,没有的话再计算z^2,算是对longlong溢出的特判#include<bits/stdc++.h>typedeflonglongll;usingnamespacestd;classUnionFind{public:UnionFind(ll......
  • 【GiraKoo】夜神模拟器提示“当前设备未开启VT”
    【解决】夜神模拟器提示“当前设备未开启VT”环境Windows11夜神模拟器64位现象启动夜神模拟器时,提示“检测到当前设备未开启VT,请先开启VT后再运行64位模拟器”原因首先,需要按照VT教程,检查BIOS是不是真的没有开启VT功能。如果当前已经开启了VT。但是依然无法运行夜神。......
  • YC359D [ 20241029 CQYC NOIP 模拟赛 T4 ] 平方(square)
    题意与P9994相同。模数改为\(998244353\)。Sol有点魔怔了。注意到我们代码中存在:if(siz[x]<=bsk){for(autok:idx[x]){isl[sy[k]]-=val[k];val[k]=1ll*val[k]*val[k]%mod;isl[sy[k]]+=val[k];}}这段内层会......