AtCoder Regular Contest 093
AtCoder Regular Contest 093
C - Traveling Plan
题意:
给定n个点,求出删去i号点时,按顺序从起点到一号点走到n号点最后回到起点所走的路程是多少。
\(n\le 2e5\)
分析:
可以通过观察发现,无论删去那个点,比全部都走所差距的距离都是\(|a_i-a_{i-1}|-|a_{i+1}-a_i|+|a_{i-1}-a_{i+1}|\)
所以直接枚举即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+7;
int a[MAXN],n,ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n+1;i++) ans+=abs(a[i]-a[i-1]);
for(int i=1;i<=n;i++){
cout<<ans-abs(a[i]-a[i-1])-abs(a[i]-a[i+1])+abs(a[i-1]-a[i+1])<<endl;
}
}
D - Grid Components
题意:
求一个边长均小于等于100的矩阵,涂成黑白两色,使得白色连通块和黑色连通块的数量分别是A、B。输出这个矩阵的长宽,并输出这个矩阵的具体涂色方案('.'是白色,'#'是黑色)。
\(1\le A,B \le 500\)
分析:
经过观察发现100×100的矩阵一定可以满足所有要求。
那么这么构造,首先把上下两部分平分开,上面是白色下面是黑色,然后一个一个的在白色里填充黑色,黑色同理。
填充方法是从头开始每隔一行一列就填一个,可以证明这样填上下两个矩阵每个矩阵都可以填600个以上所以可以轻松满足要求。
#include <bits/stdc++.h>
using namespace std;
int a,b;
char ch[201][201];
int main()
{
for(int i=1;i<=50;i++)
for(int j=1;j<=100;j++)
ch[i][j]='.';
for(int i=51;i<=100;i++)
for(int j=1;j<=100;j++)
ch[i][j]='#';
cin>>a>>b;
a--;b--;
int cnt1=0,cnt2=0;
for(int i=1;i<=50;i+=2){
if(cnt1==b) break;
for(int j=1;j<=100;j+=2){
if(cnt1==b) break;
cnt1++;
ch[i][j]='#';
}
}
for(int i=52;i<=100;i+=2){
if(cnt2==a) break;
for(int j=1;j<=100;j+=2){
if(cnt2==a) break;
cnt2++;
ch[i][j]='.';
}
}
cout<<"100 100"<<endl;
for(int i=1;i<=100;i++){
for(int j=1;j<=100;j++){
cout<<ch[i][j];
}
cout<<endl;
}
}
E - Bichrome Spanning Tree
题意:
给出一个N个点M条边的无向图,现在要给一些边染黑色或者白色,现在从这些边中选出一些边生成若干棵生成树,要求这些生成树至少包含一条白色的边和一条黑色的边,而且这些生成树的最小值是X,问有多少种染色方法。
分析:
先求一下最小生成树并且设其权值和为s。
那么分情况讨论一下,对于\(s>x\) 的情况,明显答案是0;
对于\(s=x\)的情况,我们可以确定求得那棵树一定是这个最小生成树,所以记录一下所有在最小生成树中的边。那么这些边之外的边就可以随意染色,然后保证最小生成树中的边不是全黑或者全白就可以了。容易得出答案是\(2^{M-m}\times(2^m-1)\)种。
首先必须把S里面的的所有边涂成黑色(或者白色),先假设全部涂成黑色,那么剩余的边,对每一条边单独处理,对边e,它染成白色添加进去之后,如果和集合S中的边形成的满足题意的最小生成树权重w′小于X,那么这条边只能继续涂成黑色;如果w′大于X,它涂成黑色白色都无所谓;然后把w′等于X的边的数量统计下来,假设有tot条边,可以知道这tot条边只要有一条边是白色就行了,因为只要选中这里面的一条边,剩余的边肯定都是从集合S中选出来了, 不可能再从其它边中选取,如果必须涂成黑色的有d条边, 那么答案就是2M−tot−d∗(2tot−1),最终结果再乘以2就行了,就是必须涂成一种颜色的有两种涂法
(借鉴自:AtCoder Regular Contest 093(E-Bichrome Spanning Tree))
#include <bits/stdc++.h>
using namespace std;
const int MAXN=2010;
#define ll long long
#define int ll
const int mo=1e9+7;
struct po
{
int x,y,l;
}a[MAXN];
bool cmp(po a,po b){return a.l<b.l;}
int n,m,tmp,x;
int f[MAXN];
int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
int kruskal(int now)
{
int res=0;
for(int i=1;i<=n;i++) f[i]=i;
if(now) {res+=a[now].l;f[a[now].x]=a[now].y;}
for(int i=1;i<=m;i++){
int r1=find(a[i].x),r2=find(a[i].y);
if(r1==r2) continue;
res+=a[i].l;
f[r1]=r2;
}
return res;
}
inline int power(int x,int k)
{
int cnt=1;
while(k){
if(k&1) cnt=cnt*x%mo;
k>>=1;
x=x*x%mo;
}
return cnt;
}
int val,cnt1,cnt2;
int ans;
main()
{
cin>>n>>m>>x;
for(int i=1;i<=m;i++) cin>>a[i].x>>a[i].y>>a[i].l;
sort(a+1,a+m+1,cmp);
tmp=kruskal(0);
if(tmp>x){
cout<<0;
return 0;
}
if(tmp==x){
for(int i=1;i<=m;i++){
val=kruskal(i);
if(val==x) cnt1++;
else cnt2++;
}
ans=((power(2,cnt1)-2)*power(2,cnt2)%mo+mo)%mo;
} else {
for(int i=1;i<=m;i++){
val=kruskal(i);
if(val==x) cnt1++;
else if(val>x) cnt2++;
}
ans=(2*(power(2,cnt1)-1)%mo*power(2,cnt2)%mo+mo)%mo;
}
cout<<ans;
}
F - Dark Horse
题意:
有2N个选手参与一场比赛,比赛规则是:相邻的两个人比赛一次,败者淘汰掉,胜者继续进行,直到只剩一个人为止。现在给出1号选手会败给哪些选手并且已知其他选手之间均满足:两个选手比赛,编号小的一定会胜利。现在可以安排每个选手初始的位置,要钦定1号选手最后获胜,求能满足条件的初始位置的方案数。
分析:
由于可以明显发现1号无论站在哪里最后的胜负方案数都是一样的,所以可以直接就让1号在一号位置,然后将答案最后乘上\(2^n\)即可。
考虑1号一定会打n场,而且每次都会和\(2^n+1...2^{n+1}\)中编号最小的人打,所以保证其中没有人在ad这个集合内就可以了。
以下部分摘自dalao:
接下来就是本题最有趣的地方了:容斥原理。
设S为N个块的一个子集,\(f(S)\)表示这个子集中所有块的最小值均在A的范围内(其余块是否在A的范围内不考虑)的方案数。
这样一来,最终答案即为\(∑(−1)^{|S|}f(S)\)
将A从大到小排序,依次考虑加入\(A_i\)后的\(f(S)\)。
1、成为一个块的最小值:那么在这个块中,必须填充相应数量的,编号比AiAi大的选手,组合数统计。
2、不成为块的最小值,不操作即可\((dp[i][S]+=dp[i−1][S])\)。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const int MAXN=20;
const int MAXM=2<<16+1;
const int mo=1e9+7;
int n,m,a[MAXN],bit[MAXN],c[MAXM];
int fac[MAXM],inv[MAXM],dp[MAXN][MAXM];
int power(int x,int k)
{
int cnt=1;
while(k){
if(k&1) cnt=cnt*x%mo;
x=x*x%mo;
k>>=1;
}
return cnt;
}
int C(int x,int y){return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;}
main()
{
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>a[i];
reverse(a+1,a+m+1);
for(int i=0;i<=n-1;i++) bit[i]=1<<i;
int cnt=1<<n;fac[0]=1;
for(int i=1;i<=cnt;i++)
fac[i]=1ll*fac[i-1]*i%mo;
inv[cnt]=power(fac[cnt],mo-2);
for(int i=cnt-1;i>=0;i--)
inv[i]=inv[i+1]*(i+1ll)%mo;
int u=(1<<n)-1;dp[0][0]=1;
for(int i=1;i<=m;i++)
for(int s=0;s<=u;s++){
int l=cnt-a[i]+1-s;
dp[i][s]=(dp[i][s]+dp[i-1][s])%mo;
for(int j=0;j<=n-1;j++){
if(l<bit[j]) break;
if(bit[j]&s) continue;
dp[i][s^bit[j]]=(dp[i][s^bit[j]]+(1ll*dp[i-1][s]*C(l-1,bit[j]-1))%mo*fac[bit[j]]%mo)%mo;
}
}
int ans=fac[cnt-1]; c[0]=1;
for(int s=1;s<=u;s++){
c[s]=-c[s-(s&-s)];
ans=(ans+1ll*c[s]*dp[m][s]*fac[cnt-1-s]%mo)%mo;
}
ans=(1ll*ans*cnt%mo+mo)%mo;
cout<<ans;
}