牛客小白月赛121(ABCDE)
前言
这tm叫小白月赛???!!!
一、Kato_Shoko
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{int n;cin>>n;string s;cin>>s;map<char,int>cnts;int ans=0;for(int i=0;i<n;i++){if(s[i]=='K'){if(++cnts['K']>1){ans++;}}else if(s[i]=='a'){if(++cnts['a']>1){ans++;}}else if(s[i]=='t'){if(++cnts['t']>1){ans++;}}else if(s[i]=='o'){if(++cnts['o']>3){ans++;}}else if(s[i]=='_'){if(++cnts['_']>1){ans++;}}else if(s[i]=='S'){if(++cnts['S']>1){ans++;}}else if(s[i]=='h'){if(++cnts['h']>1){ans++;}}else if(s[i]=='k'){if(++cnts['k']>1){ans++;}}else{ans++;}}if(cnts['K']<1||cnts['a']<1||cnts['t']<1||cnts['o']<3||cnts['_']<1||cnts['S']<1||cnts['h']<1||cnts['k']<1){cout<<"NO"<<endl;return ;}cout<<"YES "<<ans<<endl;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这题多少有点史了,纯模拟,不多说了……
二、白绝大军
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{ll n,t;cin>>n>>t;vector<pll>a(n+1);ll sum=0;for(int i=1;i<=n;i++){cin>>a[i].first;sum+=a[i].first;}for(int i=1;i<=n;i++){cin>>a[i].second;}if(sum>=t){cout<<0<<endl;return ;}sort(a.begin()+1,a.end());ll ans=0;for(int i=n;i>=1;i--){ll val=a[i].first;ll cnt=a[i].second;//cout<<val<<" "<<cnt<<" "<<sum<<" "<<ans<<endl;if(sum+cnt*val>t){ll cur=(t-sum+val-1)/val;sum+=cur*val;ans+=cur;break;}else{sum+=cnt*val;ans+=cnt;}}if(sum<t){cout<<-1<<endl;return ;}cout<<ans<<endl;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这个题就是一眼贪心,思路就是从大到小复制,一旦够了停就可以了。
三、重组猎魔团试炼
最暴力的一题。
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void swap(int i,int j,vector<int>&nums)
{int t=nums[i];nums[i]=nums[j];nums[j]=t;
}void dfs(int i,int d,vector<int>&cand,set<int>&ans)
{if(i==cand.size()){int val=0;for(int j=0;j<cand.size();j++){val=val*10+cand[j];}if(val%d==0){ans.insert(val);}return ;} for(int j=i;j<cand.size();j++){swap(i,j,cand);dfs(i+1,d,cand,ans);swap(j,i,cand);}
}void solve()
{int n,d;cin>>n>>d;string x;cin>>x;if(d==1){cout<<0<<endl;return ;}set<int>res;for(int s=1;s<(1<<n);s++){vector<int>cand;for(int i=0;i<n;i++){if((s>>i)&1){cand.push_back(x[i]-'0');}}// for(auto x:cand)// {// cout<<x<<" ";// }// cout<<endl;set<int>mod;dfs(0,d,cand,mod);if(mod.size()>0){res.insert(*mod.begin());}}if(!res.empty()){cout<<*res.begin()<<endl;}else{cout<<-1<<endl;}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这题这个数据范围就很有说法,拿计算器算一下就可以发现,这个数据范围下可以考虑直接状压暴力枚举所有可能选择情况,然后根据每种情况再暴力枚举所有排列的可能,这个计算量其实是可以接受的。
所以代码就是先状压暴力枚举所有可能的选择情况,然后去dfs生成全排列,如果能被d整除就加入set。之后若存在一种排列,那么就把最小值加入所有情况的set。最后若set不为空,就输出最小值即可。
四、谁敢动资本的蛋糕
最神秘的一题,最guess的一题。
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{int n;cin>>n;vector<ll>a(n+1);for(int i=1;i<=n;i++){cin>>a[i];}ll ans=0;ll cur=a[1];for(int i=2;i<=n;i++){ans+=2*(cur&a[i]);cur^=a[i];}cout<<ans<<endl;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这个题其实在赛时只观察出了最后的答案和合并的顺序没有关系,所以直接按顺序来一遍就可以了。
证明的话就是注意到x^y+2*(x&y)=x+y,因为异或表示无进位加法,而2*(x&y)可以看作将与的结果左移一位,那就表示进位信息。所以合并后数组和会减少x+y-(x^y),合并到最后剩下的数为所有数的异或和,因此总成本为累加和减去异或和,与合并顺序无关。
五、构造序列
逆天dp转移方程……
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;const int MOD=1e9+7;ll power(ll x,int n)
{ll ans=1;while(n){if(n&1){ans=(ans*x)%MOD;}x=(x*x)%MOD;n>>=1;}return ans;
}void solve()
{ll n,m;cin>>n>>m;//首先,这个问题可以拆分成两部分//1.计数有多少种严格递增的长度序列l,假设种数为P(n,k)//2.对于某个固定的长度序列,取值有多少种选择//因为第一个段有m种选择,之后每一个段必须与前一个段不同,所以总数为m*(m-1)^(k-1)//之后把两部分相乘并对所有的k求和即可//又因为分成k段最少就是1,2,3,……,k,所以有k*(k+1)/2<=n//当n为1e5时,k<=450//对于第一个问题//考虑定义dp[i][j]为把整数i划分成j个严格递增的正整数的方案数//所以dp[i][j]=P(i,j)//初始dp[0][0]=1,状态转移为dp[i][j]+dp[i-j][j-1]+dp[i-j][j]//对于最小项l1=1<l2<l3<……的情况//去掉这一个1,由于原序列严格递增且l2>=2,所以可以给每个数再减一个1//有l2-1<l3-1<……,其有j-1项,和为i-j,所以与dp[i-j][j-1]对应//对于最小项l2>=2,直接对每一项都减1//有l1-1<l2-1<l3-1……,其有j项,和为i-j,所以与dp[i-j][j]对应const int K=450;vector<vector<ll>>dp(n+1,vector<ll>(K+1));dp[0][0]=1;for(int i=1;i<=n;i++){for(int j=1;j<=min(i,K);j++){dp[i][j]=(dp[i-j][j-1]+dp[i-j][j])%MOD;}}ll ans=0;for(int k=1;k<=K;k++){if(n<k*(k+1)/2){break;}ll p1=dp[n][k];ll p2=power(m-1,k-1)*m%MOD;ans=(ans+(p1*p2)%MOD)%MOD;} cout<<ans<<endl;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这题太抽象了……
首先,这个问题可以拆分为两部分。一是计数有多少种严格递增的长度序列L,假设种类数为P(n,k)。二是对于给定的长度序列L,取值有多少种选择。所以把两部分的结果相乘,然后对所有的k求和就是最终答案。首先看第二个问题,因为第一个段有m种选择,后面每一段都必须与前面的段不同,所以一共有m*(m-1)^k种选择。又因为分成k段最少就是1,2,3……k,所以有k*(k+1)/2小于等于n,当n为1e5时,k小于等于450。
对于第一个问题,考虑定义dp[i][j]表示把整数i划分成j个严格递增的正整数的方案数,所以有dp[i][j]就等于P(i,j)。初始化dp[0][0]等于1,表示把0划分成0个有一种划分方案,状态转移为dp[i][j]等于dp[i-j][j-1]+dp[i-j][j]。这个转移的原因是,对于最小项为1的情况,即l1=1<l2<l3<……,去掉这一个1,由于原序列严格递增且l2>=2,所以可以考虑对剩余所有项都减1,有l2-1<l3-1<……。对于这个新序列,有j-1项,和为i-j,与dp[i-j][j-1]对应。对于最小项不为1的情况,考虑直接对所有项都减1,所以有l1-1<l2-1<l3-1<……,有j项,和为i-j,与dp[i-j][j]对应。
所以之后在转移时注意j不能超过K=450,然后枚举每一个k,计算dp[n][k]和m*(m-1)^k的乘积之和即可。
总结
F题真理解不了,感觉都能放div2后几题了……