2022.11.11题解
一测:\(300pts\)。
T1:预估绿(1600)。
T2:String Reversal(蓝)
T3:XOR Inverse(蓝)
T1:
题意简述:
给定一个 \(n\times m\) 的带墙体平面图,一个点有 \(\frac{1}{2}\) 的概率建造灯塔,灯塔可以照亮上下左右四个方向所有点,不能穿过墙,问被照亮的点的个数的期望值,对 \(1e9+7\) 取模。
解:
直接找到一个点上下左右所有点,如果有 \(k\) 个,那么这个点被点亮概率为 \(\frac{2^k-1}{2^k}\)。
把所有的点期望加起来即可。
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=2005;
const int M=10005;
const int mod=1000000007;
int p[M],n,m,a[N][N];
char s[N][N];
int quick_pow(int x,int y)
{
int sum=1,num=x;
while(y)
{
if(y&1)sum*=num,sum%=mod;
num*=num;
num%=mod;
y>>=1;
}
return sum;
}
signed main()
{
scanf("%lld%lld",&n,&m);
int num=1;
p[0]=0;
for(int i=1;i<=10000;i++)
{
num*=2;
num%=mod;
p[i]=((num-1+mod)%mod)*quick_pow(num,mod-2)%mod;
}
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)
{
int sum=0;
for(int j=1;j<=m;j++)
{
if(s[i][j]=='#')sum=0;
else sum++;
a[i][j]+=sum;
}
sum=0;
for(int j=m;j>=1;j--)
{
if(s[i][j]=='#')sum=0;
else sum++;
a[i][j]+=sum;
}
}
for(int j=1;j<=m;j++)
{
int sum=0;
for(int i=1;i<=n;i++)
{
if(s[i][j]=='#')sum=0;
else sum++;
a[i][j]+=sum;
}
sum=0;
for(int i=n;i>=1;i--)
{
if(s[i][j]=='#')sum=0;
else sum++;
a[i][j]+=sum;
}
}
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(s[i][j]=='.')a[i][j]-=3;
int ans=0;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)ans+=p[a[i][j]],ans%=mod;
printf("%lld",ans);
return 0;
}
T2:
题意简述:
给定一个字符串,将字符串倒置,每次操作可以交换相邻的两个字母,问最少用多少步可以将字符串还原。
解:
可以预处理出来所有字母从前往后的位置,每次从前往后还原一个字母,那么一定是将离的最近的字母转移过来,交换时会影响在这个字母之前的所有字母位置,用树状数组记录往左移了几格即可。
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=2e5+5;
int n,t[26][N],len[26],f[N];
char s1[N],s2[N];
inline int lowbit(int x)
{
return x&(-x);
}
void update(int x,int k)
{
while(x<=n)
{
f[x]+=k;
x+=lowbit(x);
}
}
int search(int x)
{
int num=0;
while(x)
{
num+=f[x];
x-=lowbit(x);
}
return num;
}
signed main()
{
scanf("%lld",&n);
scanf("%s",s1+1);
for(int i=1;i<=n;i++)s2[i]=s1[n-i+1];
for(int i=n;i>0;i--)t[s2[i]-'a'][++len[s2[i]-'a']]=i;
int ans=0;
for(int i=1;i<=n;i++)
{
ans+=search(t[s1[i]-'a'][len[s1[i]-'a']])+t[s1[i]-'a'][len[s1[i]-'a']]-i;
update(t[s1[i]-'a'][len[s1[i]-'a']]+1,-1);
update(1,1);
len[s1[i]-'a']--;
}
printf("%lld",ans);
return 0;
}
T3:
题意简述:
给定一个序列,求最小的 \(x\) 使得序列中所有数异或 \(x\) 后逆序对最少。
分位处理,对于每一位求顺序对与逆序对,然后分组向下递归。最后处理每一位,如果这一位顺序对比逆序对少,那么选取这一位来对所有数异或即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int N=3e5+5;
int n,a[N],ans,f[N],cnt,p1[35],p2[35];
struct node
{
int data,name;
}t[N],s[N];
int cmp(node fi,node se)
{
if(fi.data==se.data)return fi.name<se.name;
return fi.data<se.data;
}
int cmp2(node fi,node se)
{
return fi.name<se.name;
}
inline int lowbit(int x)
{
return x&-x;
}
void update(int x)
{
while(x)
{
f[x]++;
x-=lowbit(x);
}
}
int search(int x)
{
int sum=0;
while(x<=cnt)
{
sum+=f[x];
x+=lowbit(x);
}
return sum;
}
void dfs(int i,int beg,int ed)
{
if(beg>=ed)return;
if(i<0)return;
bool ss=((a[beg]&(1<<i)));
int bef=beg,sum1=0,sum2=0,tot1=0,tot2=0;
for(int j=beg;j<=ed;j++)
{
int x=s[j].name;
if(((a[x]&(1<<i))>0)!=ss)
{
if(ss==1)tot1+=sum2*(j-bef);
if(ss==0)tot2+=sum1*(j-bef);
ss=((a[x]&(1<<i)));
bef=j;
}
sum1+=((a[x]&(1<<i))>0),sum2+=((a[x]&(1<<i))==0);
s[j].data=((a[x]&(1<<i))>0);
}
sort(s+beg,s+ed+1,cmp);
if(ss==1)tot1+=sum2*(ed-bef+1);
if(ss==0)tot2+=sum1*(ed-bef+1);
dfs(i-1,beg,ed-sum1);
dfs(i-1,beg+sum2,ed);
p1[i]+=tot1;
p2[i]+=tot2;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]),s[i].name=i;
dfs(30,1,n);
for(int i=0;i<=30;i++)if(p2[i]>p1[i])ans^=(1<<i);
for(int i=1;i<=n;i++)
{
t[i].name=i;
t[i].data=a[i]^ans;
}
sort(t+1,t+1+n,cmp);
int bef=-1;
for(int i=1;i<=n;i++)
{
if(t[i].data!=bef)cnt++;
bef=t[i].data;
t[i].data=cnt;
}
sort(t+1,t+1+n,cmp2);
int tot=0;
for(int i=1;i<=n;i++)
{
tot+=search(t[i].data+1);
update(t[i].data);
}
printf("%lld %lld",tot,ans);
return 0;
}

浙公网安备 33010602011771号