A. 飞行棋
分析
因为这道题的期望不是模意义下的
所以在迭代多次之后精度就会达到要求
设 (f[i][j]) 为第(i) 轮在位置 (j) 的人获胜的概率
随便转移一下就行了
转移的时候记录一个变量 (p) 表示当前有多少概率所有人都没赢
如果 (p<1e-8) 直接退出循环
注意当前的位置的人获胜的概率还要乘上其它的人在这个人之前不获胜的概率
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=163;
int n,a[maxn],b[maxn],m;
double f[2][maxn],ans[maxn],p=1.0,gl[maxn];
int main(){
n=read(),m=read();
for(rg int i=1;i<=n;i++) a[i]=read();
for(rg int i=1;i<=m;i++) b[i]=read(),gl[i]=1.0;
a[n+1]=a[n+2]=a[n+3]=a[n+4]=a[n+5]=n;
rg int now=0;
f[now][n]=1.0;
for(rg int i=1;;i++){
now^=1;
memset(f[now],0,sizeof(f[now]));
for(rg int j=1;j<n;j++){
for(rg int k=1;k<=6;k++){
f[now][j]+=f[now^1][a[j+k]];
}
f[now][j]/=6.0;
}
for(rg int j=1;j<=m;j++){
if(f[now][b[j]]){
p/=gl[j];
ans[j]+=p*f[now][b[j]];
gl[j]-=f[now][b[j]];
p*=gl[j];
}
}
if(p<1e-8) break;
}
for(rg int i=1;i<=m;i++) printf("%.6f
",ans[i]);
return 0;
}
B. 字符串难题
分析
把问题转化一下:求所有情况下本质不同的前缀个数之和
发现这个东西不太好求,而且 (n) 的范围只有 (20),所以考虑容斥
可以把本质不同的前缀看成集合的并,把公共前缀看成集合的交
那么答案就是元素个数为 (1) 的集合的交减去元素个数为 (2) 的集合的交加上元素个数为 (3) 的集合的交(cdots)
设 (E(lcp(S))) 为集合 (S) 中公共前缀的期望
那么最终的答案就是 (sumlimits_{Ssubseteq A}(-1)^{|S|-1}E(lcp(s)))
只要求出 (E(lcp(S))) 就行了
这个东西我们可以枚举每一个位置的贡献
设 (f[i]) 为 (S) 中的所有串的公共前缀长度为 (i),同时不考虑 (i) 以后的部分的影响的情况下的方案
设所有串中共含有 (totcnt) 个 (?),当前已经遍历了 (nowcnt) 个 (?)
如果所有 (S) 集合中的串第 (i) 位都是 (?)
那么可以全填 (0) 或者全填 (1),显然 (f[i]=2 imes f[i-1])
如果只能全填 (0) 或者全填 (1)
那么 (f[i]=f[i-1])
如果第 (i) 位上既有 (0) 又有 (1),即发生了冲突,就不能转移,方案数置为 (0)
最终的贡献 (E(lcp(S))=sumlimits_{i=1}^{min(len[i],i in S)}f[i] imes 2^{totcnt-nowcnt})
因为 (trie) 树上还有一个空节点,所以最后还要把把前缀为空的贡献 (2^{totcnt}) 加上
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e3+5,mod=998244353;
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
char s[maxn][maxn];
int n,len[maxn],totcnt,ans,mmax,mi[maxn];
int main(){
n=read();
for(rg int i=1;i<=n;i++){
scanf("%s",s[i]+1);
len[i]=strlen(s[i]+1);
for(rg int j=1;j<=len[i];j++){
if(s[i][j]=='?') totcnt++;
}
}
mi[0]=1;
for(rg int i=1;i<=totcnt;i++) mi[i]=addmod(mi[i-1],mi[i-1]);
mmax=(1<<n)-1,ans=mi[totcnt];
rg int siz,minlen,nans,jud0,jud1,ncnt;
for(rg int i=1;i<=mmax;i++){
siz=0,minlen=0x3f3f3f3f,nans=0,ncnt=totcnt;
for(rg int j=1;j<=n;j++){
if(i&(1<<(j-1))) siz++,minlen=std::min(minlen,len[j]);
}
for(rg int j=1;j<=minlen;j++){
jud0=jud1=0;
for(rg int k=1;k<=n;k++){
if(i&(1<<(k-1))){
if(s[k][j]=='0')jud0=1;
else if(s[k][j]=='1')jud1=1;
else ncnt--;
}
}
if(jud0 && jud1) break;
if(!jud0 && !jud1) ncnt++;
nans=addmod(nans,mi[ncnt]);
}
if(siz&1) ans=addmod(ans,nans);
else ans=addmod(ans,mod-nans);
}
printf("%d
",ans);
return 0;
}
C. 障碍雷达
分析
首先可以发现,收益是个初值为 (0) 的二次函数,代价是一次函数
那么可以猜想必然存在一种最优解,使得每个雷达要么选最大的纵坐标,要么不选
然后就有了 (n^2) 的做法
然后就有了 (300n) 的做法
发现左区间更新右区间时,左边按照右端点排序右边按照左端点排序后,决策点单调
就可以用类似于 (CDQ) 分治的方式来做了
时间复杂度是 (nlog^2n) 的
把排序函数直接写在 (sort) 里超级快
代码
300n做法
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int t,n;
long long f[maxn],ans;
struct asd{
int l,r;
long long val;
}b[maxn];
bool cmp(rg asd aa,rg asd bb){
return aa.r<bb.r;
}
long long pf(rg long long aa){
return aa*aa;
}
int main(){
t=read();
while(t--){
n=read();
rg int aa,bb,cc;
for(rg int i=1;i<=n;i++){
aa=read(),bb=read(),cc=read();
b[i].l=aa-bb,b[i].r=aa+bb,b[i].val=4LL*pf(bb)-4LL*bb*cc;
}
std::sort(b+1,b+1+n,cmp);
for(rg int i=1;i<=n;i++) f[i]=0;
for(rg int i=1;i<=n;i++){
f[i]=std::max(f[i],b[i].val);
for(rg int j=std::max(1,i-250);j<i;j++){
f[i]=std::max(f[i],f[j]+b[i].val-pf(std::max(0,b[j].r-b[i].l)));
}
for(rg int j=1;j<100;j++){
f[i]=std::max(f[i],f[j]+b[i].val-pf(std::max(0,b[j].r-b[i].l)));
}
}
ans=0;
for(rg int i=1;i<=n;i++) ans=std::max(ans,f[i]);
if(ans%4==0) printf("%lld.00
",ans/4);
else if(ans%4==1) printf("%lld.25
",ans/4);
else if(ans%4==2) printf("%lld.50
",ans/4);
else printf("%lld.75
",ans/4);
}
return 0;
}
分治做法
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int t,n;
long long ans;
struct asd{
int l,r,x;
long long val,dp;
}b[maxn];
long long pf(rg long long aa){
return aa*aa;
}
void solve(rg int l,rg int r,rg int L,rg int R){
if(l>r) return;
rg int mids=(l+r)>>1,now=L;
rg long long mmax=-1e18;
for(rg int i=L;i<=R;++i){
if(mmax<b[i].dp+b[mids].val-pf(std::max(0,b[i].r-b[mids].l))){
mmax=b[i].dp+b[mids].val-pf(std::max(0,b[i].r-b[mids].l));
now=i;
}
}
b[mids].dp=std::max(b[mids].dp,mmax);
solve(l,mids-1,L,now);solve(mids+1,r,now,R);
}
void cdq(rg int l,rg int r){
if(l==r) return;
rg int mids=(l+r)>>1;
cdq(l,mids);
std::sort(b+l,b+mids+1,[](rg asd aa,rg asd bb){return aa.r<bb.r;});
std::sort(b+mids+1,b+r+1,[](rg asd aa,rg asd bb){return aa.l<bb.l;});
solve(mids+1,r,l,mids);
cdq(mids+1,r);
}
int main(){
t=read();
while(t--){
n=read();
rg int aa,bb,cc;
for(rg int i=1;i<=n;i++){
aa=read(),bb=read(),cc=read();
b[i].l=aa-bb,b[i].r=aa+bb,b[i].val=4LL*pf(bb)-4LL*bb*cc,b[i].x=aa;
}
std::sort(b+1,b+1+n,[](rg asd aa,rg asd bb){return aa.x<bb.x;});
for(rg int i=1;i<=n;i++) b[i].dp=std::max(0LL,b[i].val);
ans=0;
cdq(1,n);
for(rg int i=1;i<=n;i++) ans=std::max(ans,b[i].dp);
if(ans%4==0) printf("%lld.00
",ans/4);
else if(ans%4==1) printf("%lld.25
",ans/4);
else if(ans%4==2) printf("%lld.50
",ans/4);
else printf("%lld.75
",ans/4);
}
return 0;
}