20240731

赛时得分

题目 A B C D 总分 排名 比例
满分 100 100 100 100 400 167 100%
得分 40 28 70 10 148 80 47.9%

A. 矩阵(100/100)

\(\text{40%}\) 得分做法,用两个一维数组记录一下行和列的修改倍数,不变的情况倍数是 \(1\),然后直接每个点求值再乘上行倍数和列倍数即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int mod=1e9+7,N=1001,M=1e6+1;
ll n,m,k,x,y,a[N][N],ans=0;
ll h[M],l[M];
char op;
void sub2()
{
	for(int i=1;i<=n;i++) h[i]=1;
	for(int i=1;i<=m;i++) l[i]=1;
	while(k--)
	{
		cin>>op;
		cin>>x>>y;
		if(op=='R') h[x]=h[x]*y%mod;
		else l[x]=l[x]*y%mod;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			ans+=(((i-1)*m%mod+j)%mod)*h[i]%mod*l[j]%mod;
		}
	}
	cout<<ans%mod;
	return;
}
int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>k;
	sub2();
	return 0;
}

\(\text{100%}\) 得分做法,不难发现我们求完行倍数数组 \(h_i\) 和列倍数数组 \(l_j\) 后,所求的答案就变成了:\(\sum\limits^{n}_{i=1}\sum\limits^{m}_{j=1}\cdot\ h_i\cdot l_j\cdot (j+(i-1)\cdot m)\)。我们尝试化简这个式子:

\[\sum\limits^{n}_{i=1}\sum\limits^{m}_{j=1}\cdot\ h_i\cdot l_j\cdot (j+(i-1)\cdot m) \]

\[=\sum\limits^{n}_{i=1}h_i\cdot \sum\limits^{m}_{j=1}l_j\cdot (j+(i-1)\cdot m) \]

\[=\sum\limits^{n}_{i=1}h_i\cdot \sum\limits^{m}_{j=1} (l_j\cdot j+l_j\cdot (i-1)\cdot m) \]

\[=\sum\limits^{n}_{i=1}h_i\cdot( \sum\limits^{m}_{j=1} l_j\cdot j+ \sum\limits^{m}_{j=1} l_j\cdot (i-1)\cdot m) \]

\[=\sum\limits^{n}_{i=1}h_i\cdot (i-1)\cdot m\cdot \sum\limits^{m}_{j=1}l_j+\sum\limits^{n}_{i=1}h_i\cdot \sum\limits^{m}_{j=1}j\cdot l_j \]

化简到这里我们已经实现了每个 \(\sum\) 上的参数分离,这样我们只需动态处理 \(\sum^{n}_{i=1}h_i\),而预处理 \(\sum^{m}_{j=1}j\cdot l_j\)\(\sum^{m}_{j=1} l_j\),就可以实现复杂度的降维,那么这里复杂度是 \(\mathcal{O}(n+m+k)\) 的。

特别要说的是,赛后此题的数据扩到了 1e9,这个做法也会被卡掉,需要提前等差数列算 sum 而不是循环预处理,将复杂度优化至 \(\mathcal{O}(k)\)。当然这个我没试,但感觉不太会,因为我的 \(h,l\) 数组的初始化已经 \(\mathcal{O}(n+m)\) 了,难绷。不过这个做法是可以过赛时数据的,我们算它满分做法。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int mod=1e9+7,N=1001,M=1e6+1;
ll n,m,k,x,y,ans,sumj,sumlj;
ll h[M],l[M];
char op;
int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) h[i]=1;
	for(int i=1;i<=m;i++) l[i]=1;
	while(k--)
	{
		cin>>op;
		cin>>x>>y;
		if(op=='R') h[x]=h[x]*y%mod;
		else l[x]=l[x]*y%mod;
	}
	for(int j=1;j<=m;j++) sumj=(sumj+l[j])%mod,sumlj=(sumlj+(j*l[j])%mod)%mod;
	for(int i=1;i<=n;i++) ans=((ans%mod+h[i]%mod*(i-1)%mod*m%mod*sumj)%mod+(h[i]%mod*sumlj)%mod)%mod;
	cout<<ans%mod;
	return 0;
}

B. 神奇的度度熊(36/100)

\(\text{28%}\) 得分做法,对于任意两个串 \(a,b\),用 KMP 查询串 \(b\)\(a\) 中出现的最小位置 \(\text{minn}\) 和最大位置 \(\text{maxn}\),其中 \(a,b\) 长度为 \(l_a,l_b\)。如果满足 \(\text{minn}=1\)\(\text{maxn}=l_a-l_b+1\),则证明 \(b\)\(a\) 的前缀和后缀。

开始暴力,从第一个串开始枚举它后面的串,若满足第一个串是后面任一个串的子串,则方案数加一,取所有串中的最大方案数即可。

不过这个方法我觉得应该能拿到 \(\text{44%}\) 的得分,因为我的测试点中有 WA 的 \(\text{16%}\),剩下才是 TLE。很迷惑。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e6+1;
ll n,nxt[N],n1,m,cnt,maxx;
string s[N];
char a[N],b[N];
void change(string s,char a[])
{
	for(int i=0;i<=s.length();i++) a[i+1]=s[i];
	return;
}
void getnxt()
{
	nxt[1]=0;
	ll j=0;
	for(int i=1;i<m;i++)
	{
		while(j and b[i+1]!=b[j+1]) j=nxt[j];
		if(b[i+1]==b[j+1]) j++;
		nxt[i+1]=j;
	}
	return;
}
bool kmp()
{
	ll j=0,minn=n+1,maxn=-1,ans;
	for(int i=0;i<n1;i++)
	{
		while(j and a[i+1]!=b[j+1]) j=nxt[j];
		if(a[i+1]==b[j+1]) j++;
		if(j==m)
		{
			ans=i+2-m;
			minn=min(ans,minn);
			maxn=max(ans,maxn);
			j=nxt[j];
		}
	}
	if(minn==1 and maxn==n1-m+1) return 1;
	else return 0;
}
int main()
{
	freopen("dudu.in","r",stdin);
	freopen("dudu.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>s[i];
	for(int i=1;i<=n;i++)
	{
		cnt=1;
		for(int j=i+1;j<=n;j++)
		{
			m=s[i].length(),n1=s[j].length();
			change(s[i],b),change(s[j],a);
			getnxt();
			if(kmp()==1) cnt++;
		}
		maxx=max(cnt,maxx);
	}
	cout<<maxx;
	return 0;
}

\(\text{36%}\) 得分做法,其实这个做法就是正解做法,但是不知道为啥程序莫名挂掉了。首先我们对于所有字符串计算它们的 hash 值然后扔到一个 multiset 中,边读入边查询,我们枚举一个 \(len\),首先判断当前字符串 \(s_i\) 的长度为 \(len\) 的前后缀是否相等,如果相等则继续判断这个前缀的 hash 值是否在 multiset 中存在过,若存在过就把 \(ans\) 加上存在的次数即可。

在线做的目的是满足 \(i<j\) 的条件。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
#define ull unsigned long long
using namespace std;
const int base=127,N=2e6+1;
ll n,l,ans,maxn;
string s[N];
ull h[N],p[N],h1,h2;
multiset<ull> hsh;
void makehash(string s,ull h[],ull p[])
{
    p[0]=1;
    ull l=s.length()-1;
    for(int i=1;i<=l;i++)
    {
        h[i]=h[i-1]*base+s[i];
        p[i]=p[i-1]*base;
    }
    return;
}
ull check(string s,ull h[],ull p[],ll l,ll r)
{
    return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
    freopen("dudu.in","r",stdin);
    freopen("dudu.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        s[i]=" "+s[i];
        ans=0;
        l=s[i].length()-1;
        makehash(s[i],h,p);
        hsh.insert(check(s[i],h,p,1,l));
        for(int len=1;len<=l;len++)
        {
            h1=check(s[i],h,p,1,len),h2=check(s[i],h,p,l-len+1,l);
            if(h1==h2)
            {
                if(hsh.find(h1)!=hsh.end()) ans+=hsh.count(h1);
            }
        }
        maxn=max(maxn,ans);
    }
    cout<<maxn;
    return 0;
}

C. 乐乐的栈游戏(70/100)

这道题算是这场的意外之喜,本来只寻思能拿到 \(\text{30%}\) 的部分分,但没想到一发 vector + multiset 直接把我送到 \(\text{70pts}\)。我只能说,STL 大法真牛逼。

\(\text{70%}\) 得分做法,其实没啥好说的,我们用一个 vector 数组来模拟栈,用一个 multiset 维护一下查询操作即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=3e5+1;
ll n,v,w,l,cnt;
char op;
vector<ll> a[N];
multiset<ll> s;
int main()
{
	freopen("stack.in","r",stdin);
	freopen("stack.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>op>>v;
		a[i]=a[v];
		if(op=='a') a[i].push_back(i);
		else if(op=='b')
		{
			l=a[i].size();
			cout<<a[i][l-1]<<endl;
			a[i].pop_back();
		}
		else
		{
			cnt=0;
			s.clear();
			cin>>w;
			for(auto j:a[w]) s.insert(j);
			for(auto j:a[v])
			{
				if(s.find(j)!=s.end()) cnt++;
			}
			cout<<cnt<<endl;
		}
	}
	return 0;
}

D. Analysis of Set Union Algorithms(10/100)

考试的时候没读懂纠缠到底是什么意思,讲题才发现这是个形容词不是动词,不过还好赛时超九成的选手都没读懂,这不亏。于是写了 \(\text{10%}\) 的部分分,开一个 set<string> 扔进去查询就行了。

posted @ 2024-07-31 14:31  Lithium_Chestnut  阅读(9)  评论(0)    收藏  举报