0420 C组模拟赛 题解
T1 题解
首先容易发现具体填哪些数并不影响期望,我们就不考虑这一部分。
考虑权值 $2^{R+C}$ 的意义。
发现等价于当前情况包含哪几种其余情况。
所以权值和等价于所有情况总共被多少种情况包含。
枚举 $R,C,k$ 利用组合数求解($k$ 为矩阵中涂黑多少个格子,上下界很容易算)。
$$\sum\limits_{R=0}^n\sum\limits_{C=0}^n\dbinom{n}{R}\cdot\dbinom{n}{C}\cdot\dbinom{n\times n -(R+C)\times n+R\times C }{k-(R+C)\times n+R\times C}$$
预处理 $\binom{n}{i}$ 和 $\binom{n\times n-i}{k-i}$ 即可,复杂度 $O(n^2)$
code
code
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#define mem(a,b) memset(a,b,sizeof(a))
#include<vector>
#define pbk(a) emplace_back(a)
#define forup(i,s,e) for(register int i=(s);i<=(e);i++)
#define fordown(i,s,e) for(register int i=(s);i>=(e);i--)
using namespace std;
#define gc getchar()
inline int read(){
int x=0,f=1;char c;
while(!isdigit(c=gc)) if(c=='-') f=-1;
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=gc;}
return x*f;
}
#undef gc
const int N=305;
long double Cn[N],Cmk[N*N],ans;
int n,m,k;
signed main()
{
n=read(),m=read(),k=read();
Cn[0]=1;
forup(i,1,n){
Cn[i]=Cn[i-1]*(n-i+1)/i;
}
Cmk[0]=1;
forup(i,1,k){
Cmk[i]=Cmk[i-1]*(k-i+1)/(m-i+1);
}
forup(i,0,n){
forup(j,0,n)
{
int zy=i*n+j*n-i*j;
if(zy>k) continue;
ans+=Cn[i]*Cn[j]*Cmk[zy];
}
}
if(ans>=1e99) ans=1e99;
printf("%.15Lf",ans);
}
T2 题解
首先我们可以进行离散化,先把安全点排序。对于每种矿物,预处理出可以采到它的安全点左右端点,这一步可以利用二分 $O(m\log m+n \log m)$ 解决。
接下来我们考虑如何不重不漏地计算列表数,容易想到,可以利用某种方法将矿物排序后,对于每种矿物,计算出一定包含它,且仅包含编号小于等于它的矿物的列表数量。假设当前矿物与编号小于它的 $k$ 种矿物相交,那么它产生的贡献就是 $2^{k}$ 。
接下来考虑如何计算 $k$,这是一个类似于二维偏序的过程,我们把矿物按 $l$ 升序排列,利用一个树状数组维护每个矿物影响的安全点。每次查询时,只需要查询点 $l_i$ 和几个区间重合,因为按 $l$ 升序后就不会出现前面的区间与当前矿物有交,但不覆盖点 $l$ 的情况了。
复杂度 $O(n\log n)$
code
code
#include<iostream>
#include<algorithm>
#include<string.h>
#define mem(a,b) memset(a,b,sizeof(a))
#include<vector>
#include<set>
#define pbk(a) emplace_back(a)
#define forup(i,s,e) for(register int i=(s);i<=(e);i++)
#define fordown(i,s,e) for(register int i=(s);i>=(e);i--)
#define abs(x) (((x)^((x)>>63))-((x)>>63))
using namespace std;
#define gc getchar()
inline int read(){
int x=0,f=1;char c;
while(!isdigit(c=gc)) if(c=='-') f=-1;
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=gc;}
return x*f;
}
#undef gc
const int N=1e5+5,inf=0x3f3f3f3f,mod=998244353;
int n,m,a[N],ans;
int ksm(int a,int b){
int c=1;
while(b){
if(b&1) c=(1ll*a*c)%mod;
a=(1ll*a*a)%mod;
b>>=1;
}
return c;
}
struct Node{
int l,r;
}s[N];
bool cmp(Node a,Node b){
if(a.l!=b.l) return a.l<b.l;
return a.r<b.r;
}
struct Tree{
int c[N];
void upd(int x,int k){for(;x<=m;x+=x&-x)c[x]=(c[x]+k)%mod;}
int sum(int x){int res=0;for(;x>0;x-=x&-x)res=(res+c[x])%mod;return res;}
}mt;
signed main(){
n=read();m=read();
forup(i,1,n){
s[i].l=read();s[i].r=read();
}
forup(i,1,m){
a[i]=read();
}
sort(a+1,a+m+1);
m=unique(a+1,a+m+1)-a-1;
forup(i,1,n){
s[i].l=lower_bound(a+1,a+m+1,s[i].l)-a;
s[i].r=upper_bound(a+1,a+m+1,s[i].r)-a-1;
}
sort(s+1,s+n+1,cmp);
forup(i,1,n){
if(s[i].l>s[i].r) continue;
ans=(ans+ksm(2,mt.sum(s[i].l)))%mod;
mt.upd(s[i].l,1);mt.upd(s[i].r+1,-1);
}
printf("%d",ans);
}
T3 题解
20pts
暴搜。
40pt
区间 DP,设 $dp_{i,j}$ 表示区间 $[i,j]$ 能否转换,转移方程很好推,不细讲。
70pts
考虑我们如何判断一整个字符串能否转换。
容易想到,用一个栈来维护,从前往后遍历一遍,每次读到元素和栈顶不同就入栈,相同就出栈。假如读完一个字符串栈空了说明可以转化。
推广一下,假如 $i$ 处的栈和 $j$ 处的相同($j < i$)那么区间 $[j+1,i]$ 就是一个可转化的区间。
我们用哈希维护一下没个位置的栈,对于每个位置,$O(n)$ 地扫一遍前面的看有没有一样的栈,每有一个答案就加一,复杂度 $O(n^2)$。
100pts
用一个 map
来维护一下每个哈希值出现的次数,类似于 $70$ 分做法,复杂度 $O(n\log n)$。
需要注意,这道题数据大了之后容易出现哈希冲突,可以利用 pair
用双哈希解决。
code
code
#include<iostream>
#include<algorithm>
#include<string.h>
#define mem(a,b) memset(a,b,sizeof(a))
#include<vector>
#include<stack>
#include<map>
#define pbk(a) emplace_back(a)
#define mkp(a,b) make_pair(a,b)
#define forup(i,s,e) for(register int i=(s);i<=(e);i++)
#define fordown(i,s,e) for(register int i=(s);i>=(e);i--)
#define abs(x) (((x)^((x)>>63))-((x)>>63))
using namespace std;
#define gc getchar()
inline int read(){
int x=0,f=1;char c;
while(!isdigit(c=gc)) if(c=='-') f=-1;
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=gc;}
return x*f;
}
#undef gc
using ll=long long;
const int N=1e6+5,inf=0x3f3f3f3f,P=13331,mod1=1e9+7,mod2=1e9+9;
int ksm(int a,int b,int p){
int c=1;
while(b){
if(b&1) c=1ll*c*a%p;
a=1ll*a*a%p;
b>>=1;
}
return c;
}
int n;
ll ans;
char s[N];
ll hush1,hush2;
stack<char> stk;
map<pair<ll,ll>,int> mp;
signed main(){
scanf(" %s",s+1);
n=strlen(s+1);
int inv1=ksm(13331,mod1-2,mod1),inv2=ksm(13331,mod2-2,mod2);
mp[mkp(0,0)]++;
forup(i,1,n){
if(stk.empty()||stk.top()!=s[i]){
stk.push(s[i]);
hush1=1ll*(hush1*P+s[i]-'a'+1)%mod1;
hush2=1ll*(hush2*P+s[i]-'a'+1)%mod2;
}else{
stk.pop();
hush1=1ll*(hush1+mod1-(s[i]-'a'+1))*inv1%mod1;
hush2=1ll*(hush2+mod2-(s[i]-'a'+1))*inv2%mod2;
}
ans+=mp[mkp(hush1,hush2)];
mp[mkp(hush1,hush2)]++;
}
printf("%lld",ans);
}