一.总结
兰子姐姐的题还是比较能够让人看到希望的,但是由于 cout double 的 bug ,在F和G浪费了太多时间,还有各种细节都没注意到,都没时间做完。
本次怀疑人生:F/G/J
二.题解
A.AOE还是单体?
样例的解释明显告诉我们如何去计算,就是判断使用一次群体攻击的花费是否大于单独攻击 k 次的花费,然后我们就可以根据 x 来决定使用多少次群体攻击。
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
ll a[manx],s[manx];
// ll a[N][N];
int main(){
io;
ll n,x; cin>>n>>x;
for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[i];
sort(a+1,a+1+n);
ll ans=0;
if(x<=n){
for(int i=n-x;i<=n;i++)
ans+=a[i]-a[n-x];
ans+=a[n-x]*x;
}
else ans=s[n];
cout<<ans;
return 0;
} B.k-size字符串
F和G浪费太多时间导致没时间写 太菜了 赛后补的题
题目就是给 n 个字符 a 和 m 个字符 b,求 k size 字符。
很明显就是隔板问题,但是要注意有两种情况,一种是ab开头的类型,一种是ba开头的类型。
公式:
以 ab 为例子来说:
- a 的话就是有 n-1 个间隔(因为有 n 个 a),然后我们可以选取
个隔板放下去,这样就分成了
份 ;
- b 的话就是有 m-1 个间隔(因为有 m 个 b),然后我们可以选取
个隔板放下去,这样就分成了
份 ;
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;
ll a[70];
ll qp(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a%p*a%p;
b>>=1;
}
return ans%p;
}
ll Inv(ll x,ll p){return qp(x,p-2,p);}
ll Cal(ll n,ll m,ll p){
if(m>n) return 0;
ll ans=1;
for(int i=1;i<=m;++i)ans=ans*Inv(i,p)%p*(n-i+1)%p;
return ans%p;
}
int main(){
ll n,m,k; cin>>n>>m>>k;
if(n+m<k || k==1){
puts("0");
return 0;
}
ll ans=0;
ans+= Cal(n-1,k/2-1,mod)*Cal(m-1,k-k/2-1,mod)%mod;
ans%=mod;
ans+= Cal(m-1,k/2-1,mod)*Cal(n-1,k-k/2-1,mod)%mod;
cout<<ans%mod;
return 0;
} C.白魔法师
树形DP模板题,类似于没有上司的舞会。
以 表示以u为根的子树没有使用魔法得到的最大白色联通块的大小,
表示以u为根的子树使用魔法得到的最大白色联通块的大小。
那么很明显,我们在 dfs 的时候,只要用一个变量 cnt 来记录没有使用魔法子树的白色联通块大小和,一个变量 sb 来记录使用了魔法最大的白色联通块大小,就可以得到公式:
当 有:
当 有:
在 dfs 的过程中 ans 取 max 就好了。
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;
vector<ll>g[manx];
ll n,ans;
ll col[manx],dp[manx][4];
string s;
void dfs(ll u,ll pre){
dp[u][0]=col[u];
ll sb=-1e18,cnt=0;
for(auto v:g[u]){
if(v==pre) continue;
dfs(v,u);
cnt+=dp[v][0];
sb=max(sb,dp[v][1]-dp[v][0]);
}
if(col[u]) dp[u][0]=cnt+1,dp[u][1]=dp[u][0]+sb;
else dp[u][1]=cnt+1;
ans=max(dp[u][1],ans);
ans=max(dp[u][0],ans);
}
int main(){
cin>>n; cin>>s;
s=" "+s;
for(int i=1;i<=n;i++)
if(s[i]=='W') col[i]=1;
for(int i=2;i<=n;i++){
ll u,v; cin>>u>>v;
g[u].pb(v); g[v].pb(u);
}
dfs(1,0);
cout<<ans;
return 0;
} D.抽卡
概率数学题,算出不可能的概率,再用 1 减去就可以了。
注意减的时候可能出现负数,所以需要加个 mod 再取模。
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
const int mod=1000000007;
ll a[manx],b[manx];
ll qp(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a%p*a%p;
b>>=1;
}
return ans%p;
}
int main(){
io;
ll n; cin>>n;
ll ans=1;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
cin>>b[i];
ll x=(a[i]-b[i])*qp(a[i],mod-2,mod)%mod;
ans=ans*x%mod;
}
cout<<(1-ans+mod)%mod;
return 0;
} E.点击消除
用栈模拟字符串匹配的过程,如果遇到相同的就把连续两个弹出去~
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
char s[manx];
// ll a[N][N];
int main(){
io;
char c; ll top=0;
while(cin>>c){
s[++top]=c;
if(top>=2&&s[top-1]==s[top]){
// cout<<top<<" "<<s[top]<<" "<<c<<endl;
top-=2;
continue;
}
}
if(!top) cout<<0;
else
for(int i=1;i<=top;i++)
cout<<s[i];
return 0;
} F.疯狂的自我检索者
怀疑人生第一题。
最小情况就是剩余m个人都给1分,最大情况就是剩余m个人都给5分。
但是注意坑点!!!取消了同步就不能用 puts和print 不然就会 wa !!老是忘记这个坑点,浪费了许久。
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
double a[manx];
// ll a[N][N];
int main(){
// io;
ll n; cin>>n;
ll m; cin>>m;
double ans=0;
for(int i=1;i<=n-m;i++) cin>>a[i],ans+=a[i];
double mi=ans+m*1.0,ma=ans+m*5.0;
printf("%.5lf %.5lf",mi/n,ma/n);
return 0;
} G.解方程
怀疑人生第二题。
模板小数二分,就是判断二分的 mid 代入方程后是否比 c 大,本来代码是可以通过的,但是我用cout输出就一直wa,直到后来换成了print,好像在这题直接白给一个小时(20.13~21.21)?
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
const int mod=1000000007;
ll a,b,c;
ll pd(double x){
double ans=1.0;
for(int i=1;i<=a;i++) ans*=x;
ans+=b*1.0*log(x);
return (ans-c)>(1e-7);
}
int main(){
// io;
cin>>a>>b>>c;
double l=1.0,r=1e9;
for(int i=1;i<=100;i++){
double mid=(r+l)/2;
if(pd(mid)) r=mid;
else l=mid;
}
printf("%.8lf",l);
return 0;
} H.神奇的字母(二)
可以桶排,也可以 map 记录。
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
ll a[manx];
// ll a[N][N];
map<char,ll>v;
int main(){
io;
char c,sb; ll ans=0;
while(cin>>c){
if(c>='a'&&c<='z') v[c]++;
if(v[c]>ans) ans=v[c],sb=c;
}
cout<<sb;
return 0;
} I.十字爆破
好像做过很多次这种题目了。
- 每个点为 列贡献+行贡献-本点贡献
- 由于n*m<1e6, 所以可以开出2维数组,但是不能直接开
,要开成
。
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
// ll a[N][N];
int main(){
io;
ll n,m; cin>>n>>m;
ll a[n+1][m+1],b[n+1],c[m+1];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=b[i]=c[j]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j],b[i]+=a[i][j],c[j]+=a[i][j];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<b[i]+c[j]-a[i][j]<<" ";
}
cout<<endl;
}
return 0;
} J.异或和之和
..怀疑人生第三题,wa 了10次,赛后才发现在超出 int 的范围会变成0,应该写成
。
二进制的题目一般都是按位算贡献,在这道题我们可以通过计算 64 个二进制位出现的次数,然后用组合数学来计算对答案的贡献:
- 因为只有出现奇数次才会对答案有贡献,出现偶数次的话会抵消异或成 0 ,所以我们只考虑选取的 3 个数中在这个位为 1 的个数为 1 或者 3 的情况。
- 当该位出现次数大于等于1,设为 x,则有
个数在这个二进制上为 0 ,所以对答案的贡献为:
;
- 当该位出现次数大于等于3,设为 x, 所以对答案的贡献为:
;
代码:
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;
ll a[70];
ll qp(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a%p*a%p;
b>>=1;
}
return ans%p;
}
ll Inv(ll x,ll p){return qp(x,p-2,p);}
ll Cal(ll n,ll m,ll p){
if(m>n) return 0;
ll ans=1;
for(int i=1;i<=m;++i)ans=ans*Inv(i,p)%p*(n-i+1)%p;
return ans%p;
}
int main(){
io;
ll n; cin>>n; ll ma=0;
for(int i=1;i<=n;i++){
ll x; cin>>x; ma=max(ma,x);
for(int j=0;j<=64;j++){
ll sb=(1ll<<j);
if(sb>x) break;
if(sb&x) a[j]++;
}
}
ll ans=0;
for(int i=0;i<=64;i++){
if(!a[i]) continue;
ll x=(1ll<<i);
if(x>ma) break;
x%=mod;
if(n-a[i]>=2&&a[i]>=1) ans=(ans+a[i]%mod*Cal(n-a[i],2,mod)%mod*x%mod)%mod;
if(a[i]>=3)ans=(ans+Cal(a[i],3,mod)%mod*x%mod)%mod;
//cout<<i<<" "<<ans<<endl;
}
cout<<ans%mod;
return 0;
} 
京公网安备 11010502036488号