11.11 NOIP2024模拟赛#18 div1

T1

一眼 \(n(m-1)\)

然后发现是找循环节

直接 kmp 就行了

注意特判 \(m=1\) 的情况

善良的出题人卡了自然溢出

赛后 \(30min\) 简单证明了 kmp 不会被卡()

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define fd(i,a,b) for(int i=(a);i<=(b);i=-~i)
#define bd(i,a,b) for(int i=(a);i>=(b);i=~-i)
#define db(x) cout<<"DEBUG "<<#x<<" = "<<x<<endl;
#define endl '\n'
using namespace std;

//#define SIZE (1<<20)
//char In[SIZE],Out[SIZE],*p1=In,*p2=In,*p3=Out;
//#define getchar() (p1==p2&&(p2=(p1=In)+fread(In,1,SIZE,stdin),p1==p2)?EOF:*p1++)
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c-48);c=getchar();}
	return x*f;
}
template<typename _T>
inline void write(_T x)
{
	static int sta[35];int top=0;
	if(x<0) putchar('-'),x=-x;
	do{sta[top++]=x%10,x/=10;}while(x);
	while(top) putchar(sta[--top]+'0');
}

const int N=1e6+509,M=1e6+509,mod=998244353;

int n,m,nxt[N];
//好像随手就能卡掉kmp
//也能随手卡掉哈希
string a;

inline void init()
{
	m=read(),n=read();
	cin>>a;a=' '+a;
	fd(i,0,n+10) nxt[i]=0;
}

inline void solve()
{
	nxt[1]=0;
	for(int i=2,j=0;i<=n;i=-~i)
	{
		while(j>0&&a[i]!=a[j+1]) j=nxt[j];
		if(a[i]==a[j+1]) j++;
		nxt[i]=j;
	}
	int len=n-nxt[n];
//	cerr<<len<<endl;
	if(m==1)
	{
		string qian="",hou="";
		//可能会被卡,然鹅我不想写哈希
		fd(i,1,n)
		{
			qian+=a[i],hou=a[n-i+1]+hou;
			if(qian!=hou)
			{
				printf("%lld\n",i-1);
				return;
			}
		}
		printf("%lld\n",n-1);
	}
	else if(n%len) printf("%lld\n",n*(m-1));
	else printf("%lld\n",n*m-len);
}

void Main()
{
	init();
	solve();
}

signed main()
{
#define FJ
#ifdef FJ
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
#else
	freopen("C:/Users/Lenovo/Desktop/比赛/2024.11.11/down/string2.in","r",stdin);
	freopen("C:/Users/Lenovo/Desktop/比赛/2024.11.11/down/string.out","w",stdout);
#endif
//#define io
#ifdef io
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
#endif
	
	int T=read();
	while(T--) Main();
	
	return 0;
}

T2

第一眼发现了每个 \(\sum\) 都是二阶等差

(甚至还推了个柿子)

但是全然没有发现对角线也是二阶等差

然后只能码个暴力跑路……

然鹅多写个 if 挂没了

AC Code(lyl's)
#include<bits/stdc++.h>
using namespace std;
#define N 2000010
#define int long long 
#define mod 998244353
inline int read()
{
	int x;
	scanf("%lld",&x);
	return x;
}
inline int qpow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
int T,n,op,k,m,ans,Ans,inv2,inv6;
inline int clac1(int x)
{
	return (x*(x+1)%mod*(2*x+1)%mod*inv6)%mod;
}
inline int clac2(int x)
{
	return ((1+x)*x%mod*inv2)%mod;
}
signed main()
{
	freopen("maze.in","r",stdin);
	freopen("maze.out","w",stdout);
	T=read();
	inv2=qpow(2,mod-2),inv6=qpow(6,mod-2);
	while(T--)
	{
		ans=0;
		n=read(),op=read(),k=read();
		if(op==1)
		{
			m=n-k+1;
			ans=(((4*n*m+4*m)%mod*(m/2))%mod+(-8*n-8*m-8)%mod*clac2(m/2)%mod+16*clac1(m/2)%mod)%mod;
			ans=(ans+k*((m+1)/2))%mod;
			ans=((ans+(2*n+4-k)*(m/2))%mod-4*clac2(m/2)%mod)%mod;
		}
		else
		{
			m=n-k+1;
			ans=(((4*n*m+4*m)%mod*(m/2))%mod+(-8*n-8*m-8)%mod*clac2(m/2)%mod+16*clac1(m/2)%mod)%mod;
			ans=(ans+k*((m+1)/2))%mod;
			ans=((ans+(2*n+4-k)*(m/2))%mod-4*clac2(m/2)%mod)%mod;
			if(k!=1)
			{
				if(m&1)ans=(ans+(2*n-2)-4*(m/2))%mod;
				ans=(ans+((4*n+4)*(m/2))%mod-8*clac2(m/2)%mod)%mod;
			}
		}
		ans=(ans%mod+mod)%mod;
		Ans^=ans;
	}
	printf("%lld",Ans);
	return 0;
}

T3

感觉是 dp,但是写了个暴力润了

正解很妙:

解法一

考虑建出 Kruskal 重构树,那么一条边在最大生成树上当且仅当它两个子树不都有 \(S\) 中的点,换句话说答案就是两个子树内都有 \(S\) 中的点的边的边权和,对一条边来说这显然是一个时间轴上的后缀,差分一下就行。

解法二

离线,其实很简单,只要求出每条边在什么时刻被删即可。设 \(t_x\) 表示点 \(x\) 加入 \(S\) 的时刻。按边权从大到小的顺序把边加入,每次把边 \((x,y,z)\) 加入时,取 \(x\) 的连通块中最小的 \(t\)\(y\) 的连通块中最小的 \(t\) 两者中的较大值,就是这条边被删除的时间,用一个并查集即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define fd(i,a,b) for(int i=(a);i<=(b);i=-~i)
#define bd(i,a,b) for(int i=(a);i>=(b);i=~-i)
#define db(x) cout<<"DEBUG "<<#x<<" = "<<x<<endl;
#define endl '\n'
using namespace std;

//#define SIZE (1<<20)
//char In[SIZE],Out[SIZE],*p1=In,*p2=In,*p3=Out;
//#define getchar() (p1==p2&&(p2=(p1=In)+fread(In,1,SIZE,stdin),p1==p2)?EOF:*p1++)
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c-48);c=getchar();}
	return x*f;
}
template<typename _T>
inline void write(_T x)
{
	static int sta[35];int top=0;
	if(x<0) putchar('-'),x=-x;
	do{sta[top++]=x%10,x/=10;}while(x);
	while(top) putchar(sta[--top]+'0');
}

const int N=1e6+509,M=1e6+509,mod=998244353;

int n,w[N],p[N],fa[N];
int res,ans[N];
pair<int,int> node[N];
set< pair<int,int> > e[N];

int get(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=get(fa[x]);
}

inline void add(int x,int y)
{
	if(e[x].size()>e[y].size()) swap(x,y);
	fa[get(x)]=get(y);
	for(auto &i:e[x]) e[y].insert(i);
}

signed main()
{
#define FJ
#ifdef FJ
	freopen("party.in","r",stdin);
	freopen("party.out","w",stdout);
#else
//	freopen("C:/Users/Lenovo/Desktop/比赛/2024.11.11/down/string2.in","r",stdin);
//	freopen("C:/Users/Lenovo/Desktop/比赛/2024.11.11/down/string.out","w",stdout);
#endif
//#define io
#ifdef io
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
#endif
	
	n=read();
	fd(i,1,n) fa[i]=i;
	
	fd(i,1,n-1)
	{
		int x=read(),y=read();w[i]=read();
		node[i]={x,y};
		e[x].insert({w[i],i}),
		e[y].insert({w[i],i});
		res+=w[i];
	}
	
	fd(i,1,n) p[i]=read();
	
	bd(t,n,2)
	{
		int x,y;
		x=get(p[t]),ans[t]=res;
		auto &[c,id]=*e[x].rbegin();
		auto [t1,t2]=node[id];
		res-=c,y=get(get(t1)==get(x)?t2:t1);
		e[x].erase({c,id}),
		e[y].erase({c,id});
		add(x,y);
	}
	
	fd(i,1,n) printf("%lld\n",ans[i]);
	
	return 0;
}

T4

感觉是莫队

正解是一堆结论()

总结

  • T2 对角线没看出来,需要增强敏感性
  • T3 很妙,还是要复习一下重构树
  • T4 真 不 会
posted @ 2024-11-12 15:25  whrwlx  阅读(1)  评论(0编辑  收藏  举报