題目鏈接:https://ac.nowcoder.com/acm/contest/5600
沉寂了快三個月了,3個月來的第一篇博客~ (Minecraft mod 還沒做好,如果後面有機會的話,我可以更新mod開發教程,扯偏了 )
這場的話,手感不好,瘋狂罰時,而且寫的也很慢,最後才7題…後來發現卡我的那題居然是沒有push,還有一題被卡精度,過題人最少的那題我看都沒看,就是個排列組合而已,可惜…
牛客小白月賽A-J題解
我的程序頭(節省篇幅)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rr(x,y) read(x);read(y)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define pb(x) push_back(x)
#define sss(str) scanf("%s",str+1)
#define ssf(x) scanf("%lf",&x)
#define aLL(x) x.begin(),x.end()
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef double dd;
typedef pair<int,int> pt;
const int N=2e5+500;
const int M=2e3+50;
const int INF=0x7fffffff;
const int mod=1e9+7;
const int sz=18;
const double eps=1e-8;
template<class T>
inline void read(T &x)
{
char c;x=1;
while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
T res=c-'0';
while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
x*=res;
}
A - AOE還是單體?
當剩下的怪的數量大於等於x的時候,顯然用第二個技能是不虧甚至是賺的。
那就從一開始瘋狂用二技能,當只剩下x-1只怪時,再用第一技能唄~
int n,m;
char str[N];
int f[N];
int main()
{
rr(n,m);
FOR(i,1,n) r(f[i]);
sort(f+1,f+n+1);
LL ans=0;
if(m>n){
FOR(i,1,n){
ans+=f[i];
}
cout<<ans<<endl;
return 0;
}
for(int i=n;i>=n-m+2;i--){
ans+=f[i];
}
ans-=1ll*(m-1)*f[n-m+1];
ans+=1ll*f[n-m+1]*m;
cout<<ans<<endl;
return 0;
}
B - k-size字符串
這題就是我沒看的那題,有點可惜…
這樣想,k最小是2(此時a一邊,b一邊)k最大是多少?
(ababab這樣拍顯然利用率最高,但n和m肯定不會都那麼接近)
不難發現,和 min(a,b) 有關 LL maxx=tmp+min(max(a,b),tmp+1);
然後,肯定是a b各貢獻一半段,如果k爲奇數那就a或b多貢獻一段
怎麼算這個貢獻呢?(我用的是高中學的隔板法)
假如說有7個相同的球,我們想分爲3段,那就插3-1個板子進去,可以放的位置有7-1個(根據排列組合得: ) 以此類推即可
LL qpow(LL a,LL p)
{
LL res=1;
while(p){
if(p&1) res=res*a%mod;
a=a*a%mod;
p>>=1;
}
return res;
}
LL inv(LL x)
{
return qpow(x,mod-2);
}
LL hhh(LL n,LL m)
{
LL res=1;
FOR(i,1,m){
res=res*(n-i+1)%mod;
res=res*inv(i)%mod;
}
return res;
}
int main()
{
LL a,b,c;
rrr(a,b,c);
LL tmp=min(a,b);
LL maxx=tmp+min(max(a,b),tmp+1);
LL ans=0;
if(c>=2&&c<=maxx){
tmp=c/2;
if(c&1){
if(b>tmp){
ans=(ans+hhh(a-1,tmp-1)*hhh(b-1,tmp)%mod)%mod;
}
if(a>tmp){
ans=(ans+hhh(a-1,tmp)*hhh(b-1,tmp-1)%mod)%mod;
}
}
else{
ans=hhh(a-1,tmp-1)*hhh(b-1,tmp-1)%mod*2%mod;
}
}
cout<<ans<<endl;
return 0;
}
C - 白魔法師
這題不知道我是不是搞複雜了( bfs+並查集 )
最後因爲bfs時沒有push,瘋狂WA
思路都是,把一開始白連通塊的作爲一個集合,黑的不管。
然後找所有的黑的,假如把它變白了,那和它相連的白連通塊就能合併起來 因爲一開始記錄了,所以查詢O(1)解決
int n,m;
char str[N];
int f[N];
int dad[N];
vector<int> v[N];
bool vis[N];
int seek(int k)
{
return dad[k]==k?k:dad[k]=seek(dad[k]);
}
void bfs(int x)
{
queue<int> q;
q.push(x);
vis[x]=1;
int ans=1;
while(q.size()){
int now=q.front();
q.pop();
for(int y:v[now]){
if(!vis[y]&&str[y]=='W'){
vis[y]=1;
q.push(y);
ans++;
int xx=seek(x);
int yy=seek(y);
if(xx!=yy){
dad[xx]=yy;
}
}
}
}
f[seek(x)]=ans;
}
int main()
{
r(n);
sss(str);
FOR(i,1,n-1){
int a,b;
rr(a,b);
v[a].pb(b);
v[b].pb(a);
}
FOR(i,1,n) dad[i]=i;
FOR(i,1,n){
if(str[i]=='W'&&!vis[i]){
bfs(i);
}
}
int ans=0;
FOR(i,1,n){
ans=max(ans,f[seek(i)]);
// cout<<f[dad[i]]<<endl;
}
FOR(i,1,n){
if(str[i]=='B'){
int res=1;
for(int x:v[i]){
if(str[x]=='W'){
res+=f[dad[x]];
}
}
ans=max(ans,res);
}
}
cout<<ans<<endl;
return 0;
}
D - 抽卡
這題逆向思維,求出得不到的概率,然後用1減就ok
用到了逆元
int n,m;
char str[N];
int f[N],g[N];
LL qpow(LL a,LL p)
{
LL res=1;
while(p){
if(p&1) res=res*a%mod;
a=a*a%mod;
p>>=1;
}
return res;
}
LL inv(LL a)
{
return qpow(a,mod-2);
}
int main()
{
r(n);
FOR(i,1,n){
r(f[i]);
}
FOR(i,1,n){
r(g[i]);
}
LL ans=1;
LL a=1,b=1;
FOR(i,1,n){
a=a*(f[i]-g[i])%mod;
b=b*f[i]%mod;
ans=ans*inv(f[i])%mod;
}
cout<<(b-a+mod)%mod*ans%mod<<endl;
return 0;
}
E - 點擊消除
和括號匹配差不多
int n,m;
char str[N];
char ans[N];
int f[N];
int main()
{
sss(str);
n=strlen(str+1);
stack<char> s;
m=0;
FOR(i,1,n){
if(m>0){
if(ans[m]==str[i]){
--m;
continue;
}
}
ans[++m]=str[i];
}
if(m==0) cout<<0;
FOR(i,1,m) cout<<ans[i];
cout<<endl;
return 0;
}
F - 瘋狂的自我檢索者
最大的可能就是不知道的全部投5分,最小的就是不知道的全部投1分
int n,m;
char str[N];
int f[N];
int main()
{
rr(n,m);
LL sum=0;
FOR(i,1,n-m){
r(f[i]);
sum+=f[i];
}
double ans1=(sum+5*m)*1.0/n;
double ans2=(sum+m)*1.0/n;
printf("%.5f %.5f\n",ans2,ans1);
return 0;
}
G - 解方程
一開始就開的這題,沒想到被卡精度了
我甚至一度懷疑自己二分寫錯了(最後就判斷循環了2000次就跳出即可)
int n,m;
char str[N];
LL f[N];
int main()
{
int a,b,c;
rrr(a,b,c);
double l=0,r=1e9;
int cnt=0;
while(r-l>=eps){
cnt++;
if(cnt>=2000) break;
double mid=(l+r)/2;
// cout<<mid<<endl;
double sum=1;
FOR(i,1,a){
sum*=mid;
}
sum+=b*log(mid);
if(sum>=c){
r=mid-eps;
}
else{
l=mid+eps;
}
}
printf("%.7f\n",l);
return 0;
}
H - 神奇的字母(二)
這題輸入方式有點奇葩
int n,m;
char str[N];
int f[N];
int main()
{
while(~scanf("%s",str+1)){
int len=strlen(str+1);
for(int i=1;i<=len;i++){
// cout<<'*'<<str[i]<<'*'<<endl;
if(str[i]>='a'&&str[i]<='z'){
f[str[i]-'a'+1]++;
}
}
}
char ans='a';
int maxx=0;
FOR(i,1,26){
if(f[i]>maxx){
maxx=f[i];
ans=(char)('a'+i-1);
}
}
cout<<ans<<endl;
return 0;
}
I - 十字爆破
我這樣寫的還不夠簡潔,其實一個數組f[N]就夠了
就是個前綴和的思想(我寫的還不是前綴和,我是統計)
int n,m;
char str[N];
char ans[N];
int f[N];
LL a[N],b[N];
int get(int x,int y)
{
return f[m*x+y];
}
int main()
{
rr(n,m);
FOR(i,0,n-1){
FOR(j,0,m-1){
r(f[m*i+j]);
}
}
FOR(i,0,n-1){
a[i]=0;
FOR(j,0,m-1){
a[i]+=get(i,j);
}
}
FOR(i,0,m-1){
b[i]=0;
FOR(j,0,n-1){
b[i]+=get(j,i);
}
}
FOR(i,0,n-1){
FOR(j,0,m-1){
cout<<a[i]+b[j]-get(i,j)<<' ';
}
cout<<endl;
}
return 0;
}
J - 異或和之和
按位考慮,假如現在是第i位,有x個1,y個0 (x+y=n)
那這一位有效的情況無非就是【1 0 0】或【1 1 1】 也就是
其實好像不需要逆元,我又多此一舉了
int n,m;
char str[N];
int f[M];
LL qpow(LL a,LL p)
{
LL res=1;
while(p){
if(p&1) res=res*a%mod;
a=a*a%mod;
p>>=1;
}
return res;
}
LL inv(LL a)
{
return qpow(a,mod-2);
}
int main()
{
r(n);
FOR(i,1,n){
LL a;
r(a);
FOR(i,0,62){
if((a>>i)&1){
f[i]++;
}
}
}
LL ans=0;
FOR(i,0,62){
LL x=f[i],y=n-f[i];
if(x>=1&&y>=2){ //1 0 0
ans=(ans+(1ll<<i)%mod*x%mod*y%mod*(y-1)%mod*inv(2)%mod)%mod;
}
if(x>=3){ // 1 1 1
ans=(ans+(1ll<<i)%mod*x%mod*(x-1)%mod*(x-2)%mod*inv(6)%mod)%mod;
}
}
cout<<ans<<endl;
return 0;
}
題目不難,但我太不熟練了…