本场链接:AtCoder Beginner Contest 199
A - Square Inequality
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
int main()
{
Angel_Dust;
int a,b,c;cin >> a >> b >> c;
if(a * a + b * b < c * c) cout << "Yes
";
else cout << "No
";
return 0;
}
B - Intersection
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 105;
int a[N],b[N];
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&a[i]);
forn(i,1,n) scanf("%d",&b[i]);
int res = 0;
forn(x,1,1000)
{
bool ok = 1;
forn(i,1,n)
{
if(a[i] <= x && x <= b[i]) continue;
ok = 0;
break;
}
if(ok) ++res;
}
printf("%d
",res);
return 0;
}
C - IPFL
为了实现操作2,不妨将原来的整个字符串切割成S1
,S2
。对于操作2,直接swap(s1,s2)
即可。对于操作1,讨论位置在哪个字符串内再进行swap
即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
int main()
{
Angel_Dust;
int n;cin >> n;
string s1,s2;cin >> s1;
s2 = s1.substr(n,n);
s1.resize(n);
int q;cin >> q;
while(q--)
{
int t,a,b;cin >> t >> a >> b;
--a;--b;
if(t == 1)
{
if(a < n && b < n) swap(s1[a],s1[b]);
if(a < n && b >= n) swap(s1[a],s2[b - n]);
if(a >= n && b < n) swap(s2[a - n],s1[b]);
if(a >= n && b >= n) swap(s2[a - n],s2[b - n]);
}
else swap(s1,s2);
}
cout << s1 << s2 << endl;
return 0;
}
D - RGB Coloring 2
最粗暴的想法:直接枚举每个点的颜色,再通过图判断方案是否合法,复杂度(O(3^n))显然是不可以接受的。
如果这个图本身是联通的,那么如果某个点的颜色被确定了之后,与他相邻的点事实上只有(2)个选择,进而可以推出实际上方案数是(O(3*2^{n-1}))的,复杂度就可以承受了。枚举每个点的颜色判断是否合法即可。
那么如果图不连通怎么办?对于每个独立块求完答案后乘法原理合并即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 22,M = N * N;
vector<int> E[N];
int col[N],n,m;
ll res;
void dfs(int u,ll ans)
{
if(u == n + 1) res += ans;
else if(E[u].empty()) dfs(u + 1,ans * 3);
else
{
forn(_,1,3)
{
bool ok = 1;
for(auto& v : E[u])
if(col[v] == _)
ok = 0;
if(ok)
{
col[u] = _;
dfs(u + 1,ans);
col[u] = 0;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
forn(_,1,m)
{
int u,v;scanf("%d%d",&u,&v);
E[u].push_back(v);E[v].push_back(u);
}
dfs(1,1);
printf("%lld
",res);
return 0;
}
E - Permutation
注意到如果我们把排列的构造换成某种二元关系(可以用0/1
表达)的话,这个题可以状压表达当前的局面。
- 状态:(f[S])表示当前选择的数的集合是
S
的前提下,满足所有(x_i leq |S|)的约束条件的方案数。 - 入口:(f[0] = 1)显然
- 转移:考虑枚举新加入的数(x),那么首先(x)不属于
S
集合,其次在加入(x)之后会引入所有(x_i = |s|+1)的约数条件,直接判断这些新加入的约束条件是否仍然满足即可。 - 出口:(f[(1 << n) - 1])所有元素都选择上的情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 18;
struct Node
{
int x,y,z;
};
ll f[1 << N];
vector<Node> E[N];
int main()
{
int n,m;scanf("%d%d",&n,&m);
f[0] = 1;
forn(i,1,m)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
E[x].push_back({x,y,z});
}
forn(S,0,(1 << n) - 1)
{
forn(x,0,n - 1)
{
if(S >> x & 1) continue;
int sz = 1,ok = 1;
forn(j,0,n - 1) if(S >> j & 1) ++sz;
for(auto& lim : E[sz])
{
int cnt = 0;
if(x + 1 <= lim.y) ++cnt;
forn(j,0,n - 1) if((S >> j & 1) && j + 1 <= lim.y) ++cnt;
if(cnt > lim.z)
{
ok = 0;
break;
}
}
if(!ok) continue;
f[S | (1 << x)] += f[S];
}
}
printf("%lld
",f[(1 << n) - 1]);
return 0;
}
F - Graph Smoothing
形式非常套路的一道题,看到数据范围就可以猜到矩阵快速幂了。
首先考虑一个暴力的DP
:
- 状态:(f[i][k])表示(i)点权期望在(k)次操作之后的取值。
- 入口:(f[i][0] = a[i])
- 转移:每次随机选择一条边,那么只有两种情况:要么以(1/m)的概率选择上一条边与(i)相连,另一点记为(v),那么之后两者会取平均。要么就((m - deg_i) / m)的概率选择一条不与(i)相连的边,此后权值不变。记(S_i)为所有与(i)点有一条边直接相连的点集,那么转移方程(f[i][k] = (1/m)sumlimits_{v in S_i}(f[i][k - 1] + f[v][k - 1]) / 2 + ((m - deg_i)/m)f[i][k - 1])合并一下项可以得到:(f[i][k] = 1 / (2*m) sumlimits_{v in S_i}f[v][k - 1] + (2 * m - deg_i) / (2 * m) f[i][k - 1])。
- 出口:(f[i][k])
考虑矩阵快速幂把(k)优化掉,保留(f[i])表示(i)点的期望权值。
考虑构造系数矩阵:如果((u,v))之间有一条边直接相连,在一次转移的时候(f[u])会向(f[v])产生(f[u] / (2 * m))的贡献,则让系数矩阵(B[v][u] = 1 / (2 * m)),反之亦然。对于每个点自身的贡献:对应的直接让(B[i][i] = (2 * m - deg_i) / (2 * m))。
最后答案就是(f * B^k)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
typedef pair<int,int> pii;
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 105,MOD = 1e9+7;
pii edges[N * N];
int deg[N];
struct Mat
{
int c[N][N];
Mat()
{
memset(c,0,sizeof c);
}
Mat operator*(const Mat& r) const
{
Mat res;
forn(i,1,N - 1) forn(j,1,N - 1) forn(k,1,N - 1) res.c[i][j] = (res.c[i][j] + 1ll*c[i][k] * r.c[k][j] % MOD) % MOD;
return res;
}
void operator*(int r)
{
forn(i,1,N - 1) forn(j,1,N - 1) c[i][j] = 1ll*c[i][j] * r % MOD;
}
};
Mat qpow(Mat a,int b,int MOD)
{
Mat res;forn(i,1,N - 1) res.c[i][i] = 1;
while(b)
{
if(b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
int qpow(int a,int b,int MOD)
{
int res = 1;
while(b)
{
if(b & 1) res = 1ll * res * a % MOD;
a = 1ll * a * a % MOD;
b >>= 1;
}
return res;
}
int main()
{
int n,m,k;scanf("%d%d%d",&n,&m,&k);
Mat f,B;forn(i,1,n) scanf("%d",&f.c[1][i]);
forn(i,1,m)
{
int u,v;scanf("%d%d",&u,&v);
++deg[u];++deg[v];
edges[i] = {u,v};
}
int m_2fact = qpow(m * 2,MOD - 2,MOD);
forn(i,1,m)
{
int u = edges[i].x,v = edges[i].y;
B.c[u][v] = m_2fact;
B.c[v][u] = m_2fact;
}
forn(i,1,n) B.c[i][i] = (2 * m - deg[i]) * 1ll * m_2fact % MOD;
B = qpow(B,k,MOD);
f = f * B;
forn(i,1,n) printf("%d
",f.c[1][i]);
return 0;
}