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\) 上的参数分离,这样我们只需动态处理 \(\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> 扔进去查询就行了。

浙公网安备 33010602011771号