Codeforces Deltix Round Summer 2021 [Div.1 + Div.2]簡要題解
阿新 • • 發佈:2021-08-30
本文簡要講解了該比賽的部分題目
根據題意隨便判一判即可。
#include<bits/stdc++.h> using namespace std; int main() { int t;scanf("%d",&t); while(t--) { int c,d;scanf("%d%d",&c,&d); if((c+d)&1)puts("-1"); else if(c==0&&d==0)puts("0"); else if(c==d)puts("1"); else puts("2"); } return 0; }
這裡只考慮\(n\) 為偶數的情況,\(n\) 為奇數類似。那麼有解當且僅當奇數出現\(\dfrac n2\) 次,而且要麼全部在下標為奇數的位置上,要麼全部在下標為偶數的位置上。分別求出答案取最小值。多測小心因為中途continue之後沒有清空陣列哦。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+5; int n,a[N];vector<int>p; int main() { int t;scanf("%d",&t); while(t--) { p.clear(); scanf("%d",&n);int ct=0; for(int i=1;i<=n;++i) { scanf("%d",&a[i]); if(a[i]&1)++ct,p.push_back(i); } if(!(n&1)) { if(ct*2!=n){puts("-1");continue;} ll ans=0; for(int i=1,j=0;i<=n;i+=2,++j)ans+=abs(i-p[j]); ll res=0; for(int i=2,j=0;i<=n;i+=2,++j)res+=abs(i-p[j]); ans=min(ans,res); printf("%lld\n",ans); } else { if(ct!=n/2&&ct!=n/2+1){puts("-1");continue;} ll ans=0; if(ct==n/2) for(int i=2,j=0;i<=n;i+=2,++j)ans+=abs(i-p[j]); else for(int i=1,j=0;i<=n;i+=2,++j)ans+=abs(i-p[j]); printf("%lld\n",ans); } } return 0; }
將左括號看作向上走一步,右括號看作向下走一步,那麼括號序列合法當且僅當依次遍歷後回到原位置並且任何時刻都在原位置之上。因此直接列舉左右端點所在位置,維護最低點以及最終位置即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1005; const ll inf=1e15; int n,a[N];ll ans; int main() { scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",a+i); for(int i=1;i<=n;++i) { if(!(i&1))continue; ll dep=inf,las=0; for(int j=i+1;j<=n;++j) { if(!(j&1)) { ll mt=max(1ll,-dep),op=las+mt; ans+=max(0ll,min(a[i]-mt+1,a[j]-op+1)); } int tp=(j&1)?1:-1; las+=tp*a[j];dep=min(dep,las); } } printf("%lld\n",ans); return 0; }
關鍵性質:\(a+b=(a\&b)+(a\mid b)\) 。注意\(a+b\) 可能會爆int。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+5;
int n,k;ll a[N];
inline ll sum(int x,int y)
{
ll tt,res=0;
printf("and %d %d\n",x,y);
fflush(stdout);scanf("%lld",&tt);res=tt;
printf("or %d %d\n",x,y);
fflush(stdout);scanf("%lld",&tt);res+=tt;
return res;
}
int main()
{
scanf("%d%d",&n,&k);
ll s1=sum(1,2),s2=sum(2,3),s3=sum(1,3);
a[1]=(s1+s2+s3)/2-s2;a[2]=s1-a[1];a[3]=s3-a[1];
for(int i=4;i<=n;++i)a[i]=sum(1,i)-a[1];
nth_element(a+1,a+k,a+n+1);
printf("finish %lld\n",a[k]);
return 0;
}
首先令陣列\(c=b-a\) .手模以下樣例,發現可以將正數看作左括號連續出現次數,負數看作右括號連續出現次數。那麼是否有解等價於括號序列是否合法,而答案等於最深的巢狀層數。可以使用類似C題的思路,使用ST表維護區間最值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,q,a[N],lg[N];ll s[N],st1[20][N],st2[20][N];
inline ll gmax(int l,int r)
{
int len=lg[r-l+1];
return max(st1[len][l],st1[len][r-(1<<len)+1]);
}
inline ll gmin(int l,int r)
{
int len=lg[r-l+1];
return min(st2[len][l],st2[len][r-(1<<len)+1]);
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)scanf("%d",a+i),a[i]=-a[i];
for(int i=1,x;i<=n;++i)scanf("%d",&x),a[i]+=x;
for(int i=1;i<=n;++i)s[i]=s[i-1]+a[i],st1[0][i]=st2[0][i]=s[i];
lg[1]=0;for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
for(int i=1;i<=lg[n];++i)
for(int j=1;j+(1<<i)-1<=n;++j)
st1[i][j]=max(st1[i-1][j],st1[i-1][j+(1<<(i-1))]),
st2[i][j]=min(st2[i-1][j],st2[i-1][j+(1<<(i-1))]);
while(q--)
{
int l,r;scanf("%d%d",&l,&r);
if(s[r]-s[l-1]!=0)puts("-1");
else if(-s[l-1]+gmin(l,r)<0)puts("-1");
else printf("%lld\n",-s[l-1]+gmax(l,r));
}
return 0;
}
設全部隊的集合為\(All\) 。
回到期望的定義可知
\[ans=\sum_SF(S)\cdot|S| \]其中\(F(S)\) 代表\(S\) 集合中全部是winner的概率。\(F(S)\) 可以通過容斥求得:
\[F(S)=G(S,All\setminus S)-\sum_{T\subset S}F(T)\cdot G(S\setminus T,All\setminus S) \]其中\(G(S,T)=\prod _{u\in S}\prod_{v\in T}\frac {a_u}{a_u+a_v}\) ,即\(S\) 集合完全打敗\(T\) 集合的概率。
\(n\) 很小時應該考慮指數級做法。另外考慮概率時不要重複計算或漏掉計算某一事物的概率。
#include<bits/stdc++.h>
using namespace std;
const int N=15,mod=1e9+7;
inline int qpow(int x,int y)
{
int res=1;
for(;y;y>>=1,x=1ll*x*x%mod)
if(y&1)res=1ll*res*x%mod;
return res;
}
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void inc(int&x,int y){x=add(x,y);}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline void rec(int&x,int y){x=dec(x,y);}
inline int lb(int x){return x&(-x);}
int n,a[N],pob[N][N],lg[1<<N],res;
int H[1<<N],F[1<<N];
int main()
{
scanf("%d",&n);
for(int i=1,j=0;j<n;i<<=1,++j)lg[i]=j;
for(int i=0;i<n;++i)scanf("%d",a+i);
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
if(i^j)pob[i][j]=1ll*a[i]*qpow(a[i]+a[j],mod-2)%mod;
int st=1<<n,All=st-1;
for(int j=1;j<st;++j)
{
int k=All^j,ret=1,u=j,v;
while(u)
{
int now=lg[lb(u)];u-=lb(u);v=k;
while(v)ret=1ll*ret*pob[now][lg[lb(v)]]%mod,v-=lb(v);
}
H[j]=ret;
}
for(int i=1;i<st;++i)
{
int ans=H[i],sub=(st-1)^i;
for(int j=(i-1)&i;j;j=(j-1)&i)
{
int k=All^i,ret=1,u=j,v;
while(u)
{
int now=lg[lb(u)];u-=lb(u);v=k;
while(v)ret=1ll*ret*pob[now][lg[lb(v)]]%mod,v-=lb(v);
}
rec(ans,1ll*F[i^j]*ret%mod);
}
F[i]=ans;
inc(res,1ll*F[i]*__builtin_popcount(i)%mod);
}
printf("%d\n",res);
return 0;
}
NO PAIN NO GAIN