CodeCraft-19 and Codeforces Round #537 (Div. 2)
作者:互联网
水题,注意两个字符串可能长度不相等。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; map<char ,int >m; int main(){ m['a']=m['e']=m['i']=m['o']=m['u']=1; string s1,s2; while(cin>>s1>>s2) { int flag=1; if(s1.length()!=s2.length())flag=0; for(int i=0;i<s1.length();i++) { if(m[s1[i]]!=m[s2[i]]){ flag=0; break; } } if(flag)puts("Yes"); else puts("No"); } }View Code
B. Average Superhero Gang Power
题意:有n个人,每个人都有权值,然后你有m次操作,每次操作可以删掉一个人或者给一个人的权值加一,使得最后剩下的人平均数最大。
坑爹题,后台数据太水了,一开始以为只要删的剩下权值最大的那个一人,然后全加加给这个人就好了,居然过了。
一个半小时后被hack(感谢大哥比赛中hack的我,否则要掉分了),发现比如一组样例如果全是“7 7 7 7 7”这样的就会错,因为删人不一定是最好的选择,所以就枚举删几个人,然后取最大值就好了。
比赛刚结束我的rank1200+,rejudge后变600+,新年惨案哈哈哈。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; double a[maxn],b[maxn]; int n,m,k; int main(){ while(cin>>n>>k>>m) { for(int i=1;i<=n;i++) { scanf("%lf",&a[i]); } sort(a+1,a+1+n); for(int i=1;i<=n;i++) { b[i]=b[i-1]+a[i]; } double ans=0; double tot,po; for(int i=0;i<=m&&i<=n-1;i++) { tot=b[n]-b[i]; po=n-i; if(i<m) tot+=min(po*k,(double)m-i); ans=max(ans,tot/po); } printf("%.8f\n",ans); } }View Code
题意:有2^n个格子,格子可能是空的,也可能站着许多人,有两种操作。操作一是把某一列格子平均分成两部分,操作二是把某一列格子摧毁,如果这一列格子上没有人,那么就需要耗费A的代价,如果格子上有na个人,那么就要耗费B*na*l 的代价,问把所有格子全部摧毁的最小代价是啥。
思路:一开始完全没思路,想了很久,有人说暴力递归可以过,然后就。。试了一下?真的过了,AC后想了一下合法性。
有一个很显然的性质,就是如果一列格子全是空的,那么必定直接付出A的代价进行摧毁,如果有人的话,就要进行考虑。但是发现最多只有k个人,也就是需要考虑的次数不会太多,所以递归并不是nlogn(n=2^30),而是klogk?
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; int n,k; ll A,B; ll a[maxn]; ll dfs(ll l,ll r){ ll val=upper_bound(a,a+k,r)-lower_bound(a,a+k,l); if(val==0) return A; if(l==r) return val*B; ll mid=(l+r)>>1; ll left=dfs(l,mid),right=dfs(mid+1,r); return min(left+right,(val)*B*(r-l+1)); } int main(){ while(cin>>n>>k>>A>>B) { for(int i=0;i<k;i++) { scanf("%lld",&a[i]); } sort(a,a+k); ll ans=dfs(1,(ll)pow(2,n)); printf("%lld\n",ans); } }View Code
补。
题意:
给出一列大小写都有的字符串,字符串长度为偶数,每种字符代表一种人,给出q次查询,每次查询,都是讲所有人分成两部分序列,第x个字符代表的人和第y个字符代表的人要在同一个序列里面,问这样的方案数有多少。序列内排列不同则方案不同,两个序列顺序不同则方案不同。
思路:
设将字符串合法的分成两部分的方案数是 f 。每个字符的数量是ci,n为字符串长度,m=n/2。
则答案应该是((m)! * (m)! *f *2)/( ci! *cj !*…… )
我们发现这个式子里只有f是不知道的,f等于把x和y放入一个大小为m的背包的方案数。
如果直接证明做,时间复杂度会超时,所以我们先计算出,所有的n/2大小背包的方案数,如果我们此时把x和y的物品全部从这个背包中减去,就得到了不含x和y的背包的方案数,这个方案数和含xy是一样的。每次减完后再加回去。
而对于x和y相等的情况下,n/2的背包要么含x,要么不含x,所以此时背包数就等于f*2.
注意背包如果为空,则不要处理,否则会出错,加减的顺序也要注意。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; const ll p=1e9+7; int n,m; char s[maxn]; ll bur[60]; ll fac[maxn],inv[maxn],ans[100][100],dp[maxn],tep[maxn]; int find(char c){ if(c>='A'&&c<='Z')return c-'A'+26; return c-'a'; } void init(){ clr(bur,0),clr(dp,0),clr(inv,0); } ll qpow(ll a,ll b){ a%=p; ll res=1; while(b){ if(b&1)res*=a; res%=p; a*=a,a%=p; b>>=1; } return res; } int main(){ while(scanf("%s",s+1)!=EOF) { init(); n=strlen(s+1); for(int i=1;i<=n;i++) { bur[find(s[i])]++; } fac[0]=1; for(int i=1;i<=n;i++) { fac[i]=i*fac[i-1]%p; } ll num=fac[n/2]*fac[n/2]%p; for(int i=0;i<52;i++) { if(bur[i]==0)continue; inv[i]=qpow(fac[bur[i]],p-2); num=num*inv[i]%p; } // printf("num:%lld\n",num); dp[0]=1; for(int i=0;i<52;i++) { if(!bur[i])continue; for(int j=n/2;j>=bur[i];j--) { dp[j]+=dp[j-bur[i]]; dp[j]%=p; } } for(int i=0;i<52;i++) { ans[i][i]=dp[n/2]; } for(int i=0;i<52;i++) { if(!bur[i])continue; for(int j=0;j<=n/2;j++)tep[j]=dp[j]; for(int j=bur[i];j<=n/2;j++) { tep[j]=(tep[j]-tep[j-bur[i]]+p)%p; } for(int k=i+1;k<52;k++) { if(!bur[k])continue; for(int j=bur[k];j<=n/2;j++) { tep[j]=(tep[j]-tep[j-bur[k]]+p)%p; } ans[i][k]=ans[k][i]=tep[n/2]*2%p; for(int j=n/2;j>=bur[k];j--) { tep[j]=(tep[j]+tep[j-bur[k]])%p; } } } cin>>m; int x,y; while(m--) { scanf("%d%d",&x,&y); x=find(s[x]),y=find(s[y]); printf("%lld\n",num*ans[x][y]%p); } } }View Code
待补。
标签:CodeCraft,背包,格子,19,ll,Codeforces,long,int,maxn 来源: https://www.cnblogs.com/mountaink/p/10351663.html