郑州寄 8.6

前言

业精于勤荒于嬉,行成于思毁于随

正文(模拟赛)

卦象:凶(忘记起卦了……把 luogu 卦象赫过来)

感受:整个人都不好了。先开的 T1,然而不会,整个人直接裂开,卡了快 40min。然后开 T2,幸运地,T2 一眼秒掉了,然后由于抄式子过程中不等号写反了(最可恨的是两个小样例都能过),调了足足两个点。开 T3,显然字符串这种东西这辈子不可能写的。尽管知道它是 Manacher,尽管知道这玩意很像海亮那天讲的一个题,尽管知道维护一下哈希丢到字典树上基本上就差不多了,但根本就不敢写。因为不熟练的算法真的不敢考场上写,T4 瞄了一眼,悬着的心终于死了。三个点过去了,只获得了 100pts。咋办捏,只能回去开 T1,观测到了好多好多的性质,然而没有通往正解的道路。T1 不想去打暴力,因为暴力实在是没什么含金量,显然只有切掉才能获得一个可观的分数。然而,上天没有站在我这边,最后还是没有会 T1。静等讲题了……

隔壁软软秒了前三题,还有时间打对拍,真是菜完了

好的,好的,好的,原来 T1 是 rz 题,全世界只有云落不会 T1 的情况达成了

T1

唉,证不出来 \(ans \neq 2\) 的蒟蒻

题意

给定三个长度均为 \(n\) 的仅由小写字母构成的字符串 \(a,b,c\),试着找出两个数 \(i,j\) 满足 \(1 <= i < j < n\) 使得以下表达式最小,并输出这个最小值:

\[LCP(a_{1,...,i},b_{i+1,...,j}) + LCP(a_{i+1,...,j},c_{j+1,...,n}) + LCP(a_{1,...,i},c_{j+1,...,n}) \]

题解

赛场上观察到了答案为 \(0,1,2,3\),然而一直卡在怎么判断 \(1,2\)

如果存在 \(a,b\) 首个字符不同位置,那么直接钦定 \(b\) 的长度为 \(1\),在后面找合法的 \(c\) 即可

(找到答案就是 \(0\),找不到自然就是 \(1\) 咯!)

如果 \(b\) 总是和 \(a\) 的首字符相同,那么直接钦定 \(c\) 的字符长度为 \(1\),显然如果 \(c\)\(a,b\) 没有 LCP,答案为 \(1\),否则答案为 \(3\)

代码

短的一匹

点击查看代码
#include<bits/stdc++.h>
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=1e5+5;
int n,f[N][27];char a[N],b[N],c[N];
vi vec;
inline int cal(){
    for(int i=2;i<=n-1;i++)
        if(b[i]!=a[1])vec.pb(i);
    if(!vec.size()){
        for(int i=2;i<=n-1;i++)
            if(b[i]!=c[i+1])return 1;
        return 3;
    }
    for(int i=n;i>=3;i--){
        for(int x=0;x<=25;x++)f[i][x]=f[i+1][x];
        f[i][c[i]-'a']++;
    }

    // for(int i=3;i<=n;i++){
    //     cerr<<"Zyx "<<i<<": ";
    //     for(int x=0;x<=25;x++){
    //         if(f[i][x]!=0){
    //             cerr<<(char)(x+'a')<<"->"<<f[i][x]<<' ';
    //         }
    //     }
    //     cerr<<endl;
    // }
    
    for(int p:vec){
        for(int x=0;x<=25;x++){
            char ch=(char)(x+'a');
            if(ch==b[p]||ch==a[1])continue;
            if(f[p+1][x]>0)return 0;
        }
    }
    return 1;
}
inline void solve(){
    cin>>n>>(a+1)>>(b+1)>>(c+1);
    cout<<cal()<<'\n';
    return;
}
int main(){
    freopen("lcp.in","r",stdin);
    freopen("lcp.out","w",stdout);
    int T;cin>>T;while(T--)solve();
    return 0;
}

T2

场切了,不嘻嘻

题意

image

题解

重排方式等价于 title

剩下的部分就是二维数点板子

代码

赛时调了两个点

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,cnt1,cnt2;
struct node{int a,b;}p[N];
inline bool cmp(node s,node t){
	if(s.a!=t.a)return s.a<t.a;
	return s.b>t.b;
}
struct BIT{
	int c[N];
	inline int lb(int x){return x&(-x);}
	inline void clr(){
		for(int i=1;i<N;i++)c[i]=0;
		return;
	}
	inline void add(int x,int v){
		for(int i=x;i<N;i+=lb(i))c[i]+=v;
		return;
	}
	inline int ask(int x){
		int res=0;
		for(int i=x;i;i-=lb(i))res+=c[i];
		return res;
	}
	inline int qry(int l,int r){return ask(r)-ask(l-1);}
}bit,B,cnt;
inline int cal1(int i,int j){
	if(p[i].a>=p[j].a)return (p[i].b-p[j].b);
	return 0;
}
inline int cal2(int i,int j){
	if(p[i].a<p[j].a)return (p[i].b-p[j].b);
	if(p[i].a==p[j].a)return (p[i].b-p[j].b)*(p[i].b-p[j].b);
	return 0;
}
signed main(){
	freopen("perm.in","r",stdin);
	freopen("perm.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>p[i].a>>p[i].b;
	sort(p+1,p+n+1,cmp);
	
//	for(int i=1;i<=n;i++)cerr<<p[i].a<<' '<<p[i].b<<endl;
	
//	int ans=0;
//	for(int i=1;i<=n;i++)for(int j=1;j<=i-1;j++)ans+=cal1(i,j);
//	for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)ans+=cal2(i,j);
		
	int ans=0;
	
	for(int i=1;i<=n;i++){
		ans+=(i-1)*p[i].b; 
		ans-=bit.ask(p[i].a);
		bit.add(p[i].a,p[i].b);
	}
	
	
	for(int i=n;i>=1;i--){
		ans+=cnt.qry(p[i].a+1,N-1)*p[i].b;
		ans-=B.qry(p[i].a+1,N-1);
		B.add(p[i].a,p[i].b);
		cnt.add(p[i].a,1);
	}
	
	bit.clr();B.clr();cnt.clr();
	for(int i=n;i>=1;i--){
		ans+=cnt.qry(p[i].a,p[i].a)*p[i].b*p[i].b;
		ans-=2*p[i].b*bit.qry(p[i].a,p[i].a);
		ans+=B.qry(p[i].a,p[i].a);
		cnt.add(p[i].a,1);
		bit.add(p[i].a,p[i].b);B.add(p[i].a,p[i].b*p[i].b);
	}
	
	cout<<ans<<endl;
	
	return 0;
}

T3

谁懂从 15:00 调到 21:00 的破碎感

题意

给定大小为 \(n\) 的字符串序列 \(S\) 和大小为 \(m\) 的字符串序列 \(T\),其中 \(S\) 的第 \(i\) 个字符串为 \(S_i\)\(T\) 的第 \(j\) 个字符串为 \(T_j\)

定义一个字符串的权值 \(f(s)\)\(s\) 中奇回文子串的个数,定义两个字符串的加法 \(s+t\) 为把两个字符串拼接起来得到的新字符串,求:

\[\sum_{i=1}^{n} \sum_{j=1}^{m} f(S_i + T_j) \]

题解

容易想到答案的贡献来源于三部分:\(S_i\) 的所有奇回文子串,\(T_j\) 的所有奇回文子串,跨串的回文子串

前两个随便 Manacher 维护,最后一个比较头疼

考虑回文中心在 \(S_i\) 上,那么可以把所有的回文后缀拎出来,然后在 \(T_j\) 上跑匹配,怎么跑,字典树直接向下搜即可,直到没有转移边

代码

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define i2 __int128
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define vi vector<int>
#define vp vector<pii>
#define pb push_back
using namespace std;
const int N=1e5+2,L=2e6+2,B=107;
const ll P=1e15+37;
int n,m,p[L];ll ans;string S[N],T[N];vp vec1[N],vec2[N];
i2 hsh[L],fac[L],inv[L];
struct Trie{
    unordered_map<int,int> nxt[L];
    int tot=1,q[L];ll sum[L];i2 H[L];
    unordered_map<ll,int> mp;
    inline void ins(string s,int len){
        int now=1;
        for(int i=1;i<=len;i++){
            int x=s[i]-'a'+1;
            if(!nxt[now].count(x)){
                nxt[now][x]=++tot;
                H[tot]=(H[now]+x*fac[i]%P)%P;
                mp[(ll)(H[tot])]=tot;
            }
            now=nxt[now][x];sum[now]++;
        }
        return;
    }
    // inline void dfs(int u){
    //     if(!u)return;
    //     for(int i=1;i<=26;i++){
    //         if(nxt[u].count(i))sum[nxt[u][i]]+=sum[u];
    //         dfs(nxt[u][i]);
    //     }
    //     return;
    // }
    inline void bfs(){
        int hd=1,tl=0;q[++tl]=1;
        while(hd<=tl){
            int u=q[hd++];
            for(int i=1;i<=26;i++){
                if(!nxt[u].count(i))continue;
                sum[nxt[u][i]]+=sum[u];q[++tl]=nxt[u][i];
            }
        }
        return;
    }
    inline void clr(){
        for(int i=1;i<=tot;i++)sum[i]=H[i]=0,nxt[i].clear();
        mp.clear();tot=1;
        return;
    }
}t1,t2;
inline void Manacher(string s,int len){
    for(int i=1;i<=len;i++)p[i]=0;
    int c=0,r=0,LEN=0;
	for(int i=1;i<=len;i++){
		LEN=(r>i?min(p[c*2-i],r-i):1);
		while(i+LEN<=len&&i-LEN>=1&&s[i+LEN]==s[i-LEN])LEN++;
		if(i+LEN>r)r=i+LEN,c=i;
		p[i]=LEN;
	}
    return;
}
inline bool chk1(int l,int r,int len){
    i2 _h=(hsh[l]-hsh[r+1]+P)%P*inv[len-r]%P;

    // cerr<<"zyx "<<l<<' '<<r+1<<' '<<hsh[l]<<' '<<hsh[r+1]<<' '<<_h<<endl;

    return t2.mp[_h];
}
inline void work1(int p){
    int len=S[p].length()-1;
    for(int i=1;i<=len+1;i++)hsh[i]=0;
    for(int i=len;i>=1;i--)hsh[i]=(hsh[i+1]+(S[p][i]-'a'+1)*fac[len-i+1]%P)%P;

    // for(int i=1;i<=len;i++)cerr<<hsh[i]<<" ";
    // cerr<<endl;

    for(pii o:vec1[p]){
        int ed=o.fi-o.se;
        int l=1,r=ed,pos=ed;
        while(l<=r){
            int mid=(l+r)>>1;
            if(chk1(mid,ed,len))r=mid-1,pos=mid;
            else l=mid+1;
        }
        if(pos>ed||pos==0)continue;
        i2 _h=(hsh[pos]-hsh[ed+1]+P)%P*inv[len-ed]%P;
        ans+=t2.sum[t2.mp[_h]];
    }
    return;
}
inline bool chk2(int l,int r,int len){
    i2 _h=(hsh[r]-hsh[l-1]+P)%P*inv[l-1]%P;
    return t1.mp[_h];
}
inline void work2(int p){
    int len=T[p].length()-1;
    for(int i=0;i<=len;i++)hsh[i]=0;
    for(int i=1;i<=len;i++)hsh[i]=(hsh[i-1]+(T[p][i]-'a'+1)*fac[i]%P)%P;
    for(pii o:vec2[p]){
        int st=o.fi+o.se;
        int l=st,r=len,pos=st;
        while(l<=r){
            int mid=(l+r)>>1;
            if(chk2(st,mid,len))l=mid+1,pos=mid;
            else r=mid-1;
        }
        i2 _h=(hsh[pos]-hsh[st-1]+P)%P*inv[st-1]%P;
        ans+=t1.sum[t1.mp[_h]];
    }
    return;
}
inline void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>S[i],S[i]=' '+S[i];
    for(int i=1;i<=m;i++)cin>>T[i],T[i]=' '+T[i];
    
    ll res=0ll;
    for(int i=1;i<=n;i++){
        int len=S[i].length()-1;Manacher(S[i],len);
        for(int j=1;j<=len;j++)
            if(j+p[j]-1==len)vec1[i].pb(mkp(j,p[j]));
        for(int j=1;j<=len;j++)res+=p[j];
    }
    ans+=res*m*1ll;

    res=0ll;
    for(int i=1;i<=m;i++){
        int len=T[i].length()-1;Manacher(T[i],len);
        for(int j=1;j<=len;j++)
            if(j==p[j])vec2[i].pb(mkp(j,p[j]));
        for(int j=1;j<=len;j++)res+=p[j];
    }
    ans+=res*n*1ll;
    for(int i=1;i<=n;i++){
        int len=S[i].length()-1;string str=" ";
        for(int j=len;j>=1;j--)str+=S[i][j];
        t1.ins(str,len);
    }
    t1.bfs();
    for(int i=1;i<=m;i++){
        int len=T[i].length()-1;
        t2.ins(T[i],len);
    }
    t2.bfs();
    for(int i=1;i<=n;i++)work1(i);
    for(int i=1;i<=m;i++)work2(i);
    cout<<ans<<'\n';
    return;
}
inline i2 qpow(i2 a,ll b){
    i2 res=1;
    while(b){
        if(b&1)res=res*a%P;
        a=a*a%P;b>>=1;
    }
    return res;
}
inline void init(){
    fac[0]=1;
    for(int i=1;i<L;i++)fac[i]=fac[i-1]*B%P;
    inv[0]=1;inv[L-1]=qpow(fac[L-1],P-2);
	for(int i=L-2;i>=1;i--)inv[i]=(i2)inv[i+1]*B%P;
    return;
}
inline void clr(){
    ans=0ll;
    for(int i=1;i<=n;i++)vec1[i].clear(),vec1[i].shrink_to_fit();
    for(int i=1;i<=m;i++)vec2[i].clear(),vec2[i].shrink_to_fit();
    t1.clr(),t2.clr();
    return;
}
signed main(){
    freopen("str.in","r",stdin);
    freopen("str.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    init();
    int Case;cin>>Case;while(Case--)solve(),clr();
    return 0;
}

T4

疑似简单题?然而只是云落比较菜?

题意

image

题解

二分图判无解是显然的

对于一个无向联通块,其内部可以黑白染色

再算上其它点,答案像极了一个 01 背包问题

问题是贡献如何计算

发现可以拆分鹅和鸭的贡献,鹅的贡献是 \({a_x}^2 + a_x a_y\),而鸭的贡献是 \({a_x}^2 - a_x a_y\)

剩下的就是代码实现咯,方案可以在背包过程中做

代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=1e3+5;
int n,k,a[N],z[N][N],head[N],tot;
struct Edge{int to,nxt;}e[N*N];
int col[N],blk[N],W[N][2],V[N][2],tol,f[N][N],g[N][N];
vi vec[N];
inline void add(int u,int v){
	e[++tot]={v,head[u]};head[u]=tot;
	return;
}
inline bool dfs(int u){
	blk[u]=tol;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(col[v]!=-1){
			if(col[u]==col[v])return false;
			continue;
		}
		col[v]=col[u]^1;
		if(!dfs(v))return false;
	}
	return true;
}
inline bool check(){
	for(int i=1;i<=n;i++){
		if(col[i]==-1){
			tol++;col[i]=0;
			if(!dfs(i))return false;
		}
	}
	return true;
}
signed main(){
	freopen("ggd.in","r",stdin);
	freopen("ggd.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>z[i][j];
			
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(z[i][j]&&z[j][i])add(i,j),add(j,i);
	
	memset(col,-1,sizeof(col));
	if(!check()){cout<<"NO\n";return 0;}

	for(int i=1;i<=n;i++)vec[blk[i]].pb(i);

	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(!z[i][j])continue;
			if(z[i][j]&&z[j][i]){
				if(i>j)continue;
				V[blk[i]][col[i]]+=a[i];
				V[blk[i]][col[i]^1]+=a[j];
				continue;
			}
			int o=a[i]*a[i]+a[j]*a[j];
			if(blk[i]==blk[j]){
				if(col[i]==col[j]){
					V[blk[i]][col[i]]+=(o+2*a[i]*a[j]);
					V[blk[i]][col[i]^1]+=(o-2*a[i]*a[j]);
				}else{
					V[blk[i]][0]+=o;
					V[blk[i]][1]+=o;
				}
			}else{
				int x=a[i]*a[i]+a[i]*a[j],y=a[i]*a[i]-a[i]*a[j];
				V[blk[i]][col[i]]+=x;V[blk[i]][col[i]^1]+=y;
				x=a[j]*a[j]+a[i]*a[j],y=a[j]*a[j]-a[i]*a[j];
				V[blk[j]][col[j]]+=x;V[blk[j]][col[j]^1]+=y;
			}
		}
	}
	for(int i=1;i<=n;i++)W[blk[i]][col[i]^1]++;

	memset(f,-0x3f,sizeof(f));
	f[0][0]=0;

	for(int i=1;i<=tol;i++){
		for(int j=0;j<N;j++){
			if(j>=W[i][0]&&f[i][j]<=f[i-1][j-W[i][0]]+V[i][0]){
				f[i][j]=f[i-1][j-W[i][0]]+V[i][0],g[i][j]=0;
			}
			if(j>=W[i][1]&&f[i][j]<=f[i-1][j-W[i][1]]+V[i][1]){
				f[i][j]=f[i-1][j-W[i][1]]+V[i][1],g[i][j]=1;
			}
		}
	}
	if(f[tol][k]<=-1e15){cout<<"NO\n";return 0;}
	cout<<"YES\n";
	for(int i=tol;i>=1;i--){
		if(g[i][k]==1){
			for(int x:vec[i])col[x]^=1;
			k-=W[i][1];
		}else k-=W[i][0];
	}
	for(int i=1;i<=n;i++)cout<<col[i]<<' ';
	cout<<'\n';
	return 0;
}

小结

T3 就纯一坨,T4 还不错捏

正文(加餐)

字符串选讲,难绷

关键词:字典树、模板、Manacher

其实并没有什么可以复盘的,都是板子

剩下的几个题当天还没有来得及补,挖个坑静等 AC 后叭!

(然而全是高效进阶或者板子题)

还剩了一个题目,继续挖坑待填

后记

世界孤立我任它奚落

完结撒花!

posted @ 2025-08-07 14:38  sunxuhetai  阅读(9)  评论(0)    收藏  举报