目录
A-Floor Tiles
发现答案对于一个大小为 N × M 的平面,其曲线总数为 N + M+ 平面结构
内部环的数目
最小值必然可以没有环,可以使用全
A
或者全
B 的方式很容易构造。
最大值用类似 AB 的方式 可推出左上角为AB时的最大曲线数。最小都是N+M。
BA
构造就根据给定的一块,然后贪心得把左上的求出来,然后根据需要的曲线数量删环
#include <bits/stdc++.h>
using namespace std;
const int N = 810;
int n, m, k;
int g[N][N];
void solve()
{
cin >> n >> m >> k;
int u, v, op;
string s;
cin >> u >> v >> s;
op = s[0] - 'A';
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if((i + j) % 2 == (u + v) % 2)
g[i][j] = op;
else g[i][j] = op ^ 1;
int cnt = g[1][1] == 0 ? n + m + ((n - 1) * (m - 1) + 1) / 2 : n + m + (n - 1) * (m - 1) / 2;
if(k > cnt || k < n + m)
{
cout << "No" << '\n';
return;
}
op ^= 1;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(cnt > k)
{
if(op == 0 && g[i][j] == 0)
{
if(i + 1 <= n && j + 1 <= m)
g[i][j] ^= 1, cnt --;
}
if(op == 1 && g[i][j] == 1)
{
if(i + 1 <= n && j - 1 >= 1)
g[i][j] ^= 1, cnt --;
}
}
cout << "Yes" << '\n';
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
cout << (char)('A' + g[i][j]);
cout << '\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
cin >> T;
while(T --)
solve();
return 0;
}
B-MST
因为总的点数只有1e5
对于询问集合中点多的,直接用克鲁斯卡尔每次的复杂度是O(m)
对于询问集合中点不多的,可以用prim算法,也可以把这些点有关的边都取出来跑克鲁斯卡尔
取出有关的边的方式
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//#define int ll
const int N = 1e6 + 100;
const ll mod = 998244353;
int vis[N];
struct node {
int u, v, w;
} a[N];
bool cmp(node xx, node yy) {
return xx.w < yy.w;
}
vector<pair<int,int> >e[N];
int f[N];
int find(int x) {
if (f[x] == x)return x;
else return f[x] = find(f[x]);
}
void merge(int x, int y) {
f[find(x)] = find(y);
}
void solve() {
int n, m, q;
cin >> n >> m >> q;
for (int i = 1; i <= m; i++) {
long long u, v, w;
cin >> u >> v >> w;
a[i].u = u;
a[i].v = v;
a[i].w = w;
e[u].push_back({v,w});
e[v].push_back({u,w});
}
for(int i=1;i<=n;i++){
sort(e[i].begin(),e[i].end());
}
sort(a + 1, a + m + 1, cmp);
for(int ii=1;ii<=q;ii++){
int sz;
ll ans = 0;
cin >> sz;
if (sz == 1) {
int x;
cin>>x;
cout << 0 << '\n';
} else if (sz >= 900) {
for (int i = 1; i <= sz; i++) {
int x;
cin >> x;
vis[x] = ii;
f[x] = x;
}
int cntt=0;
for (int i = 1; i <= m; i++) {
int u = a[i].u;
int v = a[i].v;
int w = a[i].w;
if (vis[u] !=ii || vis[v] != ii)continue;
int x = find(u);
int y = find(v);
if (x == y)continue;
ans += w;
cntt++;
merge(x, y);
}
if (cntt==sz-1) {
cout << ans << '\n';
} else {
cout << -1 << '\n';
}
} else {
vector<int> v(sz+10);
for(int i=0;i<sz;i++){
cin>>v[i];
f[v[i]]=v[i];
}
vector<array<int,3> > edge;
for(int i=0;i<sz;i++){
for(int j=i+1;j<sz;j++){
int x=v[i],y=v[j];
if(y>e[x].back().first)continue;
int l=-1,r=e[x].size()-1;
while(l+1<r){
int mid=l+r>>1;
if(e[x][mid].first>=y)r=mid;
else l=mid;
}
if(e[x][r].first==y){
edge.push_back({e[x][r].second,x,y});
}
}
}
sort(edge.begin(),edge.end());
for(array<int,3> it:edge){
int fx=find(it[1]),fy=find(it[2]);
if(fx!=fy){
sz--;
ans+=it[0];
merge(fx,fy);
}
}
if(sz==1)
cout<<ans<<'\n';
else cout<<-1<<'\n';
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int _ = 1;
// cin >> _;
while (_--) solve();
return 0;
}
prim的方式
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//#define int ll
const int N = 1e5 + 100;
const ll mod = 998244353;
int vis[N];
unordered_map<int, int> p[N];
struct node {
int u, v, w;
} a[N];
bool cmp(node xx, node yy) {
return xx.w < yy.w;
}
int f[N];
int find(int x) {
if (f[x] == x)return x;
else return f[x] = find(f[x]);
}
void merge(int x, int y) {
f[find(x)] = find(y);
}
void solve() {
int n, m, q;
cin >> n >> m >> q;
for (int i = 1; i <= m; i++) {
long long u, v, w;
cin >> u >> v >> w;
a[i].u = u;
a[i].v = v;
a[i].w = w;
p[u][v]=w;
p[v][u]=w;
}
sort(a + 1, a + m + 1, cmp);
for(int ii=1;ii<=q;ii++){
int sz;
ll ans = 0;
cin >> sz;
if (sz == 1) {
int x;
cin>>x;
cout << 0 << '\n';
} else if (sz >= sqrt(N)) {
for (int i = 1; i <= sz; i++) {
int x;
cin >> x;
vis[x] = ii;
f[x] = x;
}
int cntt=0;
for (int i = 1; i <= m; i++) {
int u = a[i].u;
int v = a[i].v;
int w = a[i].w;
if (vis[u] !=ii || vis[v] != ii)continue;
int x = find(u);
int y = find(v);
if (x == y)continue;
ans += w;
cntt++;
merge(x, y);
}
if (cntt==sz-1) {
cout << ans << '\n';
} else {
cout << -1 << '\n';
}
} else {
vector<ll> ve, d(sz + 1, 1e15);
vector<int>vis2(sz + 1, 0);
for (int i = 1; i <= sz; i++) {
int x;
cin >> x;
f[x] = x;
ve.push_back(x);
}
for (int i = 0; i < sz; i++) {
for (int j = 0; j < sz; j++) {
if (i == j)continue;
if (p[ve[i]].count(ve[j])) {
merge(ve[i], ve[j]);
}
}
}
int kk = -1;
int ff = 1;
for (int i = 0; i < sz; i++) {
if (kk == -1)kk = find(ve[i]);
else if (kk != find(ve[i])) {
ff = 0;
}
}
if (ff == 0) {
cout << -1 << '\n';
continue;
}
for (int i = 1; i < sz; i++) {
if (!p[ve[0]].count(ve[i]))continue;
long long dis = p[ve[0]][ve[i]];
d[i] = dis;
}
long long mi = 1e15, idx = -1;
vis2[0] = 1;
for (int i = 0; i < sz - 1; i++) {
mi = 1e15,idx = -1;
for (int j = 0; j < sz; j++) {
if (vis2[j] == 0 && d[j] < mi) {
mi = d[j];
idx = j;
}
}
vis2[idx] = 1;
ans += d[idx];
for (int k = 0; k < sz; k++) {
if (vis2[k] ==0 && p[ve[idx]].count(ve[k]) && p[ve[idx]][ve[k]] < d[k]) {
d[k] = min(d[k], p[ve[idx]][ve[k]]);
}
}
}
cout << ans << '\n';
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int _ = 1;
// cin >> _;
while (_--) solve();
return 0;
}
G-The Set of Squares
对于000以内的数,质因子中不可能出现两个大于 32 的质数,所以根据大因子进行分组,因为好集的概念是平方,所以只需要判断每个质因子出现的次数是奇数次还是偶数次,用二进制的概念用01表示出现偶数和奇数,根据大质数进行不同的状压dp,
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define int long long
#define endl '\n'
vector<int> p = { 2,3,5,7,11,13,17,19,23,29,31 };
const int MOD = 1e9 + 7;
void Prework() {
}
void Solve() {
int n;cin >> n;
map<int, vector<array<int, 2>>> mp;
for (int i = 1;i <= n;i++) {
int x;cin >> x;
int mask = 0;
int val = 1;
for (int j = 0;j < p.size();j++) {
while (x % p[j] == 0) {
x /= p[j];
mask ^= 1 << j;
if (~mask >> j & 1) val = val * p[j];
}
}
mp[x].push_back({ mask,val });
}
int u = (1 << 12) - 1;
vector<int> dp(u + 1);dp[0] = 1;
for (auto [x, v] : mp) {
for (auto [mask, val] : v) {
auto ndp = dp;
if (x!= 1) mask |= 1 << 11;
for (int i = 0;i <= u;i++) {
int ni = i ^ mask;
int mult = val;
for (int j = 0;j < 11;j++) {
if ((mask >> j & 1) && (i >> j & 1)) mult = mult * p[j] % MOD;
}
if ((i >> 11 & 1) && (mask >> 11 & 1)) mult = mult *x % MOD;
(ndp[ni] += mult * dp[i] % MOD) %= MOD;
}
swap(ndp, dp);
}
for (int i = 1 << 11;i <= u;i++) {
dp[i] = 0;
}
}
cout << (dp[0] - 1 + MOD) % MOD << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T = 1;
//cin >> T;
Prework();
while (T--) Solve();
}
I-Red Playing Cards
被包含的两个连续段之间,里面的如果先取的话,可能会使外侧的区间的长度减少,所以可以按照长度排序,然后记下来每一种区间如果取走的最优贡献,在算长区间时可以直接用这个已经存下来的值去优化dp。
#include <bits/stdc++.h>
using namespace std;
const int N=6e3+7;
int n,a[N];
int l[N],r[N],w[N],dp[N],ans[N];
bool cmp(int a,int b){
return r[a]-l[a]<r[b]-l[b];
}
int upd(int x,int y,int c){
memset(dp,0,sizeof(dp));
for(int i=x;i<=y;i++){
if(l[a[i]]==i)
dp[r[a[i]]]=max(dp[r[a[i]]],ans[a[i]]+dp[l[a[i]]-1]);
dp[i]=max(dp[i],dp[i-1]+c);
}
return dp[y];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n*2;i++) cin>>a[i],r[a[i]]=i;
for(int i=2*n;i>=1;i--) l[a[i]]=i;
for(int i=1;i<=n;i++) w[i]=i;
sort(w+1,w+n+1,cmp);
for(int i=1;i<=n;i++) ans[w[i]]=upd(l[w[i]],r[w[i]],w[i]);
cout<<upd(1,2*n,0)<<'\n';
return 0;
}