20240519比赛总结
T1 Color
https://gxyzoj.com/d/hzoj/p/3692
显然,答案与元素的位置无关,只与个数有关
考虑每个元素能经过若干次操作变成n个的概率,记
进行一次操作后,会分成三种情况,+1,-1,和不变,所以式子是:
化简得:
去分母,得:
即:
所以,转移到n的概率为等差数列,因为
接下来考虑期望,记
根据概率,单次改变数值的概率为
因为并不是所有情况都能转移到n,所以要乘上一定的概率,并且由概率,可以知道从
记
然后按照上面的方法处理一下,就可以的到这个式子:
不断将这个式子进行代入就会得到:
将
令
则
故
所以,直接O(n)递推即可
记
代码:
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
string s;
int cnt[27];
double f[100005],n,ans;
int main()
{
cin>>s;
n=s.size();
f[1]=(n-1)*(n-1)/n;
for(int i=2;i<n;i++)
{
f[i]=2.0*f[i-1]-f[i-2]-(n-1.0)/(n-1.0*i+1.0);
}
for(int i=0;i<n;i++)
{
cnt[s[i]-'A']++;
}
for(int i=0;i<26;i++)
{
ans+=f[cnt[i]];
if(cnt[i]==n)
{
printf("0.0");
return 0;
}
}
printf("%.1lf",ans);
return 0;
}
T2 SSY的队列
https://gxyzoj.com/d/hzoj/p/2385
20pts:直接暴搜即可
70pts:因为n比较小,所以考虑状压
设
代码:
#include<cstdio>
#define ll long long
using namespace std;
const int p=1234567891;
int n,m,a[35],b[35];
ll f[100005][20];
int lowbit(int x)
{
return x & (-x);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
scanf("%d",&m);
if(n>15)
{
ll ans=1;
for(int i=1;i<=n;i++)
{
ans=ans*i%p;
}
printf("%lld",ans);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if((a[i]-a[j])%m)
{
b[i]+=(1<<(j-1));
}
}
}
int tmp=(1<<n)-1;
for(int i=1;i<=n;i++)
{
f[1<<(i-1)][i]=1;
}
for(int i=1;i<=tmp;i++)
{
if(i-lowbit(i)==0) continue;
for(int j=1;j<=n;j++)
{
if(((1<<(j-1))&i)==0) continue;
int st=(i&b[j]);
for(int k=1;k<=n;k++)
{
if(((1<<(k-1))&st)==0) continue;
f[i][j]=(f[i][j]+f[i^(1<<(j-1))][k])%p;
// printf("%d %d %d %d\n",i,j,k,f[i][j]);
}
}
}
ll sum=0;
for(int i=1;i<=n;i++)
{
sum+=f[tmp][i];
}
printf("%lld",sum%p);
return 0;
}
正解:
很明显,在
很明显,当两个数的对m取模的结果相同时,则必然不能相邻,所以可以先统计相邻的情况
可以发现,三个数的余数为3,2,2和余数为3,1,1的方案数是一样的
所以可以发现,我们其实并不关心余数额分别是多少,而是关心有多少种余数以及每种余数的个数
所以可以使用哈希记录每一步的状态,记忆化搜索
代码:
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
#define ll long long
#define ull unsigned long long
using namespace std;
const ll p=1234567891,p1=1331;
int n,m,cnt[35],tot,b[35],mx;
ll a[35],fac[35];
map<ull,ll> mp[35];
bool vis[35];
ll dfs(int x,int lst)
{
if(x>n) return 1;
memset(b,0,sizeof(b));
for(int i=1;i<=tot;i++)
{
if(i!=lst) b[cnt[i]]++;
}
ull h=cnt[0];
for(int i=0;i<=mx;i++)
{
h=h*p1+b[i];
}
h=h*p1+cnt[lst];
if(mp[x].find(h)!=mp[x].end()) return mp[x][h];
ll res=0;
if(cnt[0]>0)
{
cnt[0]--;
res=(res+dfs(x+1,0))%p;
cnt[0]++;
}
for(int i=1;i<=tot;i++)
{
if(i==lst||cnt[i]<=0) continue;
cnt[i]--;
res=(res+dfs(x+1,i))%p;
cnt[i]++;
}
mp[x][h]=res;
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
scanf("%d",&m);
for(int i=1;i<=n;i++)
{
a[i]=(a[i]%m+m)%m;
}
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
int tmp1=0;
for(int j=1;j<=n;j++)
{
if(a[j]==a[i])
{
tmp1++;
vis[j]=1;
}
}
if(tmp1==1) cnt[0]++;
else cnt[++tot]=tmp1;
mx=max(mx,tmp1);
}
mx=max(mx,cnt[0]);
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=fac[i-1]*i%p;
}
ll ans=1;
for(int i=0;i<=tot;i++)
{
ans=ans*fac[cnt[i]]%p;
}
ans=ans*dfs(1,0)%p;
printf("%lld",ans);
return 0;
}
T3 [JXOI2018] 游戏
https://gxyzoj.com/d/hzoj/p/3661
题意是按照一定顺序检查,问在查到第几个时都认真工作,求每种排列答案的和
从特殊性质开始,如果l=1,则1的位置就是最少要查的次数
如果l=2,则显然要将所有的质数都检查一次,因为除1之外没有数能够删掉质数
将它推广,就可以得到100%的数据解法,显然,找出所有无法被其他数删除的数,然后排列组合就是答案
关于如何求解,可以枚举最后一个无法被删除的数的位置,然后算前面有多少种排法,然后再对能或不能两类数的内部求排列,相乘就是答案
代码:
#include<cstdio>
#define ll long long
using namespace std;
const int p=1e9+7,N=1e7;
int l,r;
int fac[N+1],inv[N+1];
ll qpow(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%p;
x=x*x%p;
y>>=1;
}
return res;
}
ll C(int n,int m)
{
return 1ll*fac[n]*inv[m]%p*inv[n-m]%p;
}
bool vis[N+1];
int pr[2000005],m;
void prime()
{
for(int i=2;i<=N;i++)
{
if(!vis[i]) pr[++m]=i;
for(int j=1;i*pr[j]<N&&j<=m;j++)
{
vis[i*pr[j]]=1;
if(i%pr[j]==0) break;
}
}
}
int cnt;
void get_p(int l,int r)
{
for(int i=l;i<=r;i++)
{
if(!vis[i]) cnt++;
for(int j=1;i*pr[j]<N&&j<=m;j++)
{
vis[i*pr[j]]=1;
if(i%pr[j]==0) break;
}
}
}
int main()
{
fac[0]=1;
for(int i=1;i<=1e7;i++)
{
fac[i]=1ll*fac[i-1]*i%p;
}
inv[N]=qpow(fac[N],p-2);
for(int i=1e7-1;i>=0;i--)
{
inv[i]=1ll*inv[i+1]*(i+1)%p;
}
prime();
scanf("%d%d",&l,&r);
ll ans=0;
if(l==1)
{
ans=1ll*(r+1)*r/2;
ans=ans%p*fac[r-1]%p;
printf("%lld",ans);
return 0;
}
int sum=r-l+1;
for(int i=l;i<=r;i++) vis[i]=0;
get_p(l,r);
for(int i=cnt;i<=sum;i++)
{
ans+=1ll*i*C(i-1,cnt-1)%p;
ans%=p;
}
ans=ans*fac[cnt]%p*fac[sum-cnt]%p;
printf("%lld",ans);
return 0;
}
T4 [MtOI2019] 小铃的烦恼
https://gxyzoj.com/d/hzoj/p/3664
注意到
所以
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律