A Falling Asleep
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N];
string s[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>s[i]>>a[i];
string k;
cin>>k;
bool flag=false;
int ans=0;
for(int i=1;i<=n;i++)
{
if(flag) ans+=a[i];
if(s[i]==k) flag=true;
}
cout<<ans;
}
B Fusing Slimes
题目每次删除一个粘液,直到所有粘液都合并到最后一个粘液里。
共有(n-1)!种删除方式,将期望乘以(n-1)!后,就转换为了一个计数问题。
先考虑400分的做法:
第i个粘液合并到第j个粘液的方案数(i<j),此时i,j之间的j-i-1个粘液一定先被删除了
如果j<n,计算方案数可以考虑逐个插入粘液,是粘液构成一个排列。
首先先放入j-i-1个粘液构成一个排列,方案数为(j-i-1)的阶乘
第i个粘液只能在这j-i-1个粘液之后删除,只能插在当前排列的最后一个空位上
再考虑删除第j个,如果i合并到j,那么j需要在i之后删除,只能插在当前排列的最后一个空位上。
而剩余的若干个粘液可以在任意时刻删除,当前插入了j-i+1个粘液,其余若干粘液可以插在任意空位上。
插入第j-i+2个粘液可以有j-i+2个空位插入,第j-i+3有j-i+3个空位…因此方案数为1*2*…*(j-i-1)*(j-i+2)*(j-i+3)*…*(n-1)。
当j=n时,第j个不需要删除,方案数为1*2*…*(j-i-1)*(j-i+1)*(j-i+3)*…*(n-1)。
于是得到了400分的做法,枚举两个位置i,j:
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(j==n)
ans=(ans+(a[j]-a[i])*f[n-1]%mod*inv(j-i)%mod)%mod;
else ans=(ans+(a[j]-a[i])*f[n-1]%mod*inv((ll)(j-i)*(j-i+1)%mod)%mod)%mod;
随后把a[j]和-a[i]拆分一下,预处理前缀和,就得到了600分的做法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,mod=1e9+7;
int n,a[N];
ll f[N],sum[N];
ll inv(ll x){
return x<=1?x:(mod-mod/x)*inv(mod%x)%mod;}
ll qpow(ll a,ll n)
{
ll ans=1;
while(n)
{
if(n&1) ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
int main()
{
f[0]=1;
for(int i=1;i<N;i++) f[i]=f[i-1]*i%mod;
scanf("%d",&n);
for(int i=1;i<N;i++) sum[i]=(sum[i-1]+f[n-1]*inv((ll)i*(i+1)%mod)%mod)%mod;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ll ans=0;
/*//400分
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(j==n)
ans=(ans+(a[j]-a[i])*f[n-1]%mod*inv(j-i)%mod)%mod;
else ans=(ans+(a[j]-a[i])*f[n-1]%mod*inv((ll)(j-i)*(j-i+1)%mod)%mod)%mod;
*/
for(int i=1;i<n;i++)
{
ans=(ans-a[i]*(sum[n-1-i])%mod)%mod;
ans=(ans-a[i]*f[n-1]%mod*inv(n-i)%mod)%mod;
}
ans=(ans+mod)%mod;
for(int i=2;i<n;i++)
ans=(ans+a[i]*sum[i-1]%mod)%mod;;
for(int i=1;i<n;i++)
ans=(ans+a[n]*f[n-1]%mod*inv(n-i)%mod)%mod;
printf("%lld\n",ans);
}
C Cookie Distribution
因为乘了一个组合数,所以可以把问题转换为,在一个k*n的棋盘中,在第i行选择ai个格子染成红色。染完后,第j列有cj个格子被染成了红色,然后在每一列被染成红色的格子中选择一个染成蓝色的方案数。
考虑dp,dp[i][j]表示染到第i行,有j个格子已经被染成蓝色的方案数。根据每列只有一个蓝色格子和每行只能染ai个格子的限制,转移方程就可以推出来了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,mod=1e9+7;
int n,k,a[N];
ll f[N],invf[N];
ll dp[30][N];
ll C(ll n,ll m)
{
return f[n]*invf[n-m]%mod*invf[m]%mod;
}
int main()
{
f[0]=1,invf[0]=invf[1]=1;
for(int i=1;i<N;i++) f[i]=f[i-1]*i%mod;
for(int i=2;i<N;i++) invf[i]=(mod-mod/i)*invf[mod%i]%mod;
for(int i=2;i<N;i++) invf[i]=invf[i]*invf[i-1]%mod;
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++) scanf("%d",&a[i]);
dp[0][0]=1;
for(int i=1;i<=k;i++)
for(int j=0;j<=n;j++)
{
for(int k=0;k<=min(j,a[i]);k++)
dp[i][j]=(dp[i][j]+dp[i-1][j-k]*C(n-j+k,k)%mod*C(n-k,a[i]-k)%mod)%mod;
}
printf("%lld\n",dp[k][n]);
}
D Arrangement
考虑贪心逐个加入元素,假如当前加入第i个元素。
如果未加入的n-i+1个元素中某个元素的不可以接在任何数的后面
即它的度数为n-i,那么就加入它,否则考虑可以加到后面的最小的一个元素。
但此时可能出现最后一个元素无法加入的情况,于是在已经加入的元素后面提取少数元素进行全排列判断是否可行即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N],d[N],t[N<<2];
void build(int l,int r,int k)
{
if(l==r){
t[k]=d[l];return;}
int m=l+r>>1;
build(l,m,k<<1);build(m+1,r,k<<1|1);
t[k]=max(t[k<<1],t[k<<1|1]);
}
int qmx(int l,int r,int k)
{
if(l==r) return l;
int m=l+r>>1;
if(t[k<<1]>=t[k<<1|1]) return qmx(l,m,k<<1);
return qmx(m+1,r,k<<1|1);
}
void fix(int l,int r,int k,int x,int v)
{
if(l==r){
t[k]-=v;return;}
int m=l+r>>1;
if(x<=m) fix(l,m,k<<1,x,v);
else fix(m+1,r,k<<1|1,x,v);
t[k]=max(t[k<<1],t[k<<1|1]);
}
vector<int>v,s;
priority_queue<int,vector<int>,greater<int> >q,del;
bool flag=false;
bool judge()
{
v.push_back(s[0]);v.push_back(s[1]);v.push_back(s[2]);
for(int i=0;i<v.size()-1;i++)
if(a[v[i]]==v[i+1])
{
v.pop_back();v.pop_back();v.pop_back();
return false;
}
flag=true;
return true;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) q.push(i);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),d[a[i]]++;
if(n==2){
printf("-1\n");return 0;}
build(1,n,1);
for(int i=1;i<=n;i++)
{
if(t[1]==n-i) v.push_back(qmx(1,n,1)),del.push(v.back());
else
{
while(!del.empty()&&!q.empty()&&del.top()==q.top()) del.pop(),q.pop();
if(v.size()==0||a[v.back()]!=q.top()) v.push_back(q.top()),q.pop();
else
{
int s=q.top();q.pop();
while(!del.empty()&&!q.empty()&&del.top()==q.top()) del.pop(),q.pop();
if(q.size()==0) v.push_back(s);
else
{
v.push_back(q.top());q.pop();
q.push(s);
}
}
}
fix(1,n,1,v.back(),n);
fix(1,n,1,a[v.back()],1);
}
s.push_back(v.back());v.pop_back();
s.push_back(v.back());v.pop_back();
s.push_back(v.back());v.pop_back();
sort(s.begin(),s.end());
while(!judge())
if(!next_permutation(s.begin(),s.end())) break;
if(!flag){
printf("-1\n");return 0;}
for(int i=0;i<v.size();i++)
printf(i==v.size()-1?"%d\n":"%d ",v[i]);
}