牛客练习赛76
A.校园活动
链接:https://ac.nowcoder.com/acm/contest/10845/A
来源:牛客网
牛牛中学为了给本校的OIer放松心情,决定举报一场校园活动。
现在学校的共有 个OIer,学校想把他们分为一些小组进行一个团队游戏。学校先了解了一下每个同学对这个团队游戏的了解程度。
为了游戏的公平,学校需要使分组后的每一个小组内所有人对游戏的了解程度之和相等,
但同学们并不希望完全由学校来给他们分组,所以这 个人站为了一行,学校只能将队列中一段完整的子队列作为一个小组。
换句话说,如果你想让位置为 和 的人在一个小组里,你就必须让 和 之间的所有人在这个组里面。
当然,我们知道如果只有一个小组是无法进行游戏的。
你需要判断是否可以将他们成功分组进行游戏,如果能成功分组进行游戏就打印出最多能分为多少个小组,不能成功分组进行游戏(所有人都在同一个组里)打印“-1”。
/*直接从大到小枚举答案*/ #include <iostream> #include <cstdio> #include <cstring> #define maxn 1010 using namespace std; int n,a[maxn],ans,sum; char s[maxn]; int main(){ scanf("%d",&n); scanf("%s",s+1); for(int i=1;i<=n;i++){ a[i]=s[i]-'0'; sum+=a[i]; } ans=n;//分成ans个组 while(ans>=2){ int lim=sum/ans;//每组和为lim if(sum%ans!=0){ ans--; continue; } int nowSum=0; for(int i=1;i<=n;i++){ if(nowSum<lim){ nowSum+=a[i]; } if(nowSum>lim)break; if(nowSum==lim)nowSum=0; } if(nowSum!=0){ ans--; continue; } else break; } if(ans>=2) printf("%d\n",ans); else puts("-1"); system("pause"); return 0; }
B.zzugzx (vs) Kurisu
链接:https://ac.nowcoder.com/acm/contest/10845/B
来源:牛客网
zzugzx和Kurisu做游戏,轮流扔n个回合骰子,骰子的m个面是[0,m-1],由zzugzx先手
每个人需要维护一个长n的m进制数,开始两个人的n个位置都是空的
每次会随机摇到一个数,然后可以选择填充到任意一个未被填充的位置
众所周知,zzugzx和Kurisu都足够聪明,问最后zzugzx胜利的概率.
(只有最后zzugzx的数字大于Kurisu才算赢,否则Kurisu赢)
/* 记忆化搜索,模拟每回合的所有情况,将每种情况的概率相加 */ #include <iostream> #include <cstdio> #include <cstring> #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; double f[5010][5010]; bool vis[5010][5010]; int n,m; double dfs(int step,int a,int b){ if(step==0)return a>b; if(vis[a][b])return f[a][b]; vis[a][b]=1; if(step%2==0){ for(int i=1;i<=m;i++){ double mx=0; int now=a,base=i; for(int j=1;j<=n;j++){ if(now%(m+1)==0) mx=max(mx,dfs(step-1,a+base,b)); base*=m+1; now/=m+1; } f[a][b]+=mx/(double)m; } } if(step%2!=0){ for(int i=1;i<=m;i++){ double mn=1; int now=b,base=i; for(int j=1;j<=n;j++){ if(now%(m+1)==0) mn=min(mn,dfs(step-1,a,b+base)); base*=m+1; now/=m+1; } f[a][b]+=mn/(double)m; } } return f[a][b]; } int main(){ scanf("%d%d",&n,&m); printf("%.10lf\n",dfs(2*n,0,0)); system("pause"); return 0; }
C.CG的通关秘籍
链接:https://ac.nowcoder.com/acm/contest/10845/C
来源:牛客网
CG最喜欢玩的就是拼图游戏,但是他已经通关了所有拼图游戏,感觉拼图游戏已经没有了任何的乐趣。所以今天他选择玩填数游戏。
CG每次填一个的数到当前位置,如果这个位置填的数比上一次填的数要大,形成顺序,他的兴奋度会增加1点,如果这个数比上一次填的数要小,形成逆序,他的兴奋度会增加2点,如果两个数相等,那么什么都不会发生。(如果是第一次填数,同样不会发生任何事情)
CG认为如果已知他n次填的数,计算出当他填了n个数之后的兴奋度太简单了,所以想要你帮他计算一下他所有填数方案的兴奋度之和。
由于这个结果过大,将这个结果
/* 对于每一个数对,前面的数为i,则后面的数有i-1种情况比i小,有m-i种情况比i大,这个数对的贡献就为2*(i-1)+m-i=i+m-2 其它的n-2个位置一共有m^(n-2)种,于是这两个位置总的贡献就为sigma(i+m-2) * m^(n-2)
我们将这个贡献当作数对中第一个数产生的贡献,可以得出一共有n-1个数会产生这样的贡献,于是答案再乘以n-1。 sigma(i+m-2) * m^(n-2) * (n-1) */ #include <iostream> #include <cstdio> #define mod 1000000007 using namespace std; int read(){ int i=0,j=1; char ch=getchar(); while(ch<'0' || ch>'9'){if(ch=='-')j=-1;ch=getchar();} while(ch<='9' && ch>='0'){i=i*10+ch-'0';ch=getchar();} return i*j; } int Pow(int x,int y){ int res=1; while(y){ if(y&1)res=1LL*res*x%mod; x=1LL*x*x%mod; y>>=1; } return res; } int main(){ int t,n,m; t=read(); while(t--){ n=read();m=read(); int ans=3LL*m%mod*(m-1)%mod*Pow(m,n-2)%mod*(n-1)%mod; ans=1LL*ans*((mod+1)/2)%mod; printf("%d\n",ans); } system("pause"); return 0; }
D.魔物消灭计划
链接:https://ac.nowcoder.com/acm/contest/10845/D
来源:牛客网
牛客大陆上有n个城市,这些城市和连接它们的m条道路形成了一个无向连通图。
牛牛王国是牛客大陆上最大的国家,首都在x城,某一天牛牛大陆上出现了许多魔物,牛牛国王希望能消灭这些魔物。
大魔法师告诉他在大陆的一个角落y城有一个神秘祭坛,只要集齐k种宝石并且到达祭坛就能消灭大陆上的所有魔物。
k种宝石散落在牛客大陆的城市里(每种宝石的数量可能不止一个,但每个城市里最多有一种宝石),并且由于宝石的力量,拥有同种宝石的城市之间可以直接传送而不消耗任何资源。
但是王国的所有道路都已经被魔物占据了,清空每条道路所需要的资源为wiw_{i}wi(道路清空一次以后就不用再次清空),牛牛国王想要知道从王国首都x出发,拿到所有的k种宝石,并且到达祭坛y,需要消耗的最小资源。
/* 将所有相同宝石对应的节点编号为同一个,然后起点和终点分别编号,这样一共有k+2种编号 状态压缩:用k+2位数来表示当前经过了哪些点,对于每一个这样的状态,用Dijkistra维护最小花费 最终答案为min{dp[i][(1<<k+2)-1]} */ #include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <utility> #define INF 0x3f3f3f3f #define maxn 110 #define min(x,y) ((x)>(y)?(y):(x)) using namespace std; int n,m,k,num,head[maxn],dp[maxn][1<<10]; int s,t,c[maxn],f[maxn]; priority_queue<pair<int,int> >q; struct node{ int to,pre,v; }e[maxn<<4]; void Insert(int from,int to,int v){ e[++num].to=to; e[num].pre=head[from]; e[num].v=v; head[from]=num; } bool vis[maxn]; void Dij(int sta){ memset(vis,0,sizeof(vis)); while(!q.empty()){ int now=q.top().second;q.pop(); if(vis[now])continue; vis[now]=1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(dp[to][sta]>dp[now][sta]+e[i].v){ dp[to][sta]=dp[now][sta]+e[i].v; q.push(make_pair(-dp[to][sta],to)); } } } } int main(){ scanf("%d%d%d%d%d",&n,&m,&k,&s,&t); for(int i=1;i<=n;i++){ scanf("%d",&c[i]); if(c[i]&& !f[c[i]]) f[c[i]]=i; } f[k+1]=s;f[k+2]=t;k+=2; for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); if(c[x])x=f[c[x]]; if(c[y])y=f[c[y]]; Insert(x,y,z);Insert(y,x,z); } int lim=1<<k; memset(dp,INF,sizeof(dp)); for(int i=1;i<=n;i++)dp[i][0]=0; for(int i=0;i<k;i++)dp[f[i+1]][1<<i]=0; for(int sta=1;sta<lim;sta++){ for(int i=1;i<=n;i++){ for(int j=sta;j;j=(j-1)&sta) dp[i][sta]=min(dp[i][sta],dp[i][j]+dp[i][j^sta]); if(dp[i][sta]!=INF)q.push(make_pair(-dp[i][sta],i)); } Dij(sta); } int ans=INF; for(int i=1;i<=n;i++)ans=min(ans,dp[i][lim-1]); printf("%d\n",ans); system("pause"); return 0; }