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 真 不 会
本文来自博客园,作者:whrwlx,转载请注明原文链接:https://www.cnblogs.com/whrwlx/p/18541949