The 1st Universal Cup. Stage 20: India
Preface
久违的训练,然后选了场题目贼诡异的场,牢底坐穿了
这场最大的问题就是一个被过穿的了 Counting 一直出不来,然后 B 卡了贼久,N 帮祁神写了个特判结果还写漏了一个 Case,直接演完了
封榜后出了另一个构造,结果还是不会 G,最后看题解是个听都没听过的东西,这下是死透了
B. Minimize Median
思路很一眼的题,但一些神秘细节比较坑人,挂了好多发才艹过去
首先根据经典结论 \(\lfloor\frac{\lfloor\frac{x}{a}\rfloor}{b}\rfloor=\lfloor\frac{x}{ab}\rfloor\),我们可以先求出 \(f_i\) 表示乘积为 \(i\) 的最小代价
然后中位数问题一眼考虑二分答案 \(mid\),要检验的话需要求出将至少 \(\frac{n+1}{2}\) 个数变为 \(\le mid\) 的最小代价
贪心地想一下肯定是操作最小的 \(\frac{n+1}{2}\) 个数最优,并且如果我们要将 \(a_i\) 变为 \(mid\),最少要除的数为 \(\lfloor\frac{a_i}{mid+1}\rfloor+1\),简单处理一下即可
预处理 \(f_i\) 数组时需要注意超出 \(m\) 部分的处理,总复杂度 \(O((n+m)\log m)\)
#include <bits/stdc++.h>
using llsi = long long signed int;
llsi a[1000006], cost[1000006], dp[1000006];
llsi solve(int n, llsi mid) {
n = (n + 1) / 2;
llsi res = 0;
if(mid == 0)
for(int i = 1; i <= n; ++i) res += dp[a[i] + 1];
else
for(int i = 1; i <= n; ++i) {
if(a[i] <= mid) continue;
res += dp[a[i] / (mid + 1) + 1];
}
return res;
}
void work() {
llsi n, m, k; std::cin >> n >> m >> k;
for(int i = 1; i <= n; ++i) std::cin >> a[i];
for(int i = 1; i <= m; ++i) std::cin >> cost[i];
for(int i = m - 1; i >= 1; --i) cost[i] = std::min(cost[i], cost[i + 1]);
for(int i = 2; i <= m + 1; ++i) dp[i] = 0X0D00072100114514LL;
for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) {
if(i * j <= m) dp[i * j] = std::min(dp[i * j], dp[i] + cost[j]);
else {
dp[m + 1] = std::min(dp[m + 1], dp[i] + cost[j]);
break;
}
}
for(int i = m; i >= 1; --i) dp[i] = std::min(dp[i], dp[i + 1]);
// for(int i = 1; i <= m; ++i) std::cerr << cost[i] << char(i == m ? 10 : 32);
std::sort(a + 1, a + n + 1);
llsi l = 0, r = m;
while(l < r) {
llsi mid = (l + r) >> 1;
if(solve(n, mid) <= k) r = mid;
else l = mid + 1;
}
// std::cerr << solve(n, 3) << char(10);
std::cout << l << char(10);
}
int main() {
std::ios::sync_with_stdio(false);
int T; std::cin >> T; while(T--) work();
return 0;
}
D. Central Subset
队友开局讨论的,我题目都不知道
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int n, m, deg[N], dis[N]; //dis1[N], dis2[N];
int que[N], ed=-1, fr=0;
bool vis[N];
vector<int> G[N], nG[N];
void dfs1(int x) {
vis[x] = true;
for (auto v : G[x]) {
if (vis[v]) continue;
nG[x].push_back(v);
nG[v].push_back(x);
++deg[x]; ++deg[v];
dfs1(v);
}
}
// int dfs2(int x, int f, int dis[]) {
// int res = x;
// for (auto v : nG[x]) {
// if (v==f) continue;
// dis[v] = dis[x] + 1;
// int tmp = dfs2(v, x, dis);
// if (dis[tmp] > dis[res]) res = tmp;
// }
// return res;
// }
void solve() {
cin >> n >> m;
for (int i=1; i<=n; ++i) G[i].clear(), nG[i].clear(), vis[i]=false, dis[i]=0, deg[i]=0;
for (int i=1; i<=m; ++i) {
int u, v; cin >> u >> v;
G[u].push_back(v); G[v].push_back(u);
}
dfs1(1);
// printf("deg:"); for (int i=1; i<=n; ++i) printf("%d ", deg[i]); puts("");
ed=-1, fr=0;
for (int i=1; i<=n; ++i) {
if (1==deg[i]) que[++ed] = i;
vis[i] = false;
}
int sqr = (int)sqrtl(n);
// printf("sqr=%d\n", sqr);
if (sqr*sqr < n) ++sqr;
vector<int> ans;
while (fr<=ed) {
int x = que[fr++]; vis[x]=true;
if (dis[x]==sqr) ans.push_back(x), dis[x]=-1;
bool ok=false;
for (int v : nG[x]) if (!vis[v]) {
ok=true;
--deg[v], --deg[x];
dis[v] = max(dis[v], dis[x]+1);
if (1==deg[v]) que[++ed] = v;
}
if (!ok) ans.push_back(x);
// printf("x=%d vis:", x); for (int i=1; i<=n; ++i) printf("%d ", vis[i]); puts("");
// printf("x=%d dis:", x); for (int i=1; i<=n; ++i) printf("%d ", dis[i]); puts("");
// printf("x=%d deg:", x); for (int i=1; i<=n; ++i) printf("%d ", deg[i]); puts("");
}
// puts("111111");
// dis1[1] = 0; int x = dfs2(1, -1, dis1);
// dis1[x] = 0; int y = dfs2(x, -1, dis1);
// dis2[y] = 0; dfs2(y, -1, dis2);
// printf("x=%d y=%d\n", x, y);
// printf("dis2:"); for (int i=1; i<=n; ++i) printf("%d ", dis2[i]); puts("");
// for (int i=1; i<=n; ++i) {
// if (dis1[i]+dis2[i]==dis1[y] && dis1[i]%sqr==0) ans.push_back(i);
// }
sort(ans.begin(), ans.end());
ans.erase(unique(ans.begin(), ans.end()), ans.end());
cout << ans.size() << '\n';
for (int x : ans) cout << x << ' '; cout << '\n';
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0);
int t; cin >> t; while (t--) solve();
return 0;
}
F. Longest Strictly Increasing Sequence
首先注意到 \(b_i-b_{i-1}\) 的值只能是 \(0/1\),因此最后合法的整个序列一定形如 \(1,2,2,2,3,3,4,4,5\)
那么值相同的一段倒着放即可,比如上面那个例子可以放 \(1,4,3,2,6,5,8,7,9\)
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=15;
int t,n,a[N],ans[N],L[N],R[N];
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%d",&n);
for (RI i=1;i<=n;++i) scanf("%d",&a[i]);
bool flag=(a[1]==1);
for (RI i=1;i<n;++i) if (a[i+1]-a[i]!=0&&a[i+1]-a[i]!=1) { flag=0; break; }
if (!flag) { puts("NO"); continue; }
for (RI i=1;i<=10;++i) L[i]=n+1,R[i]=0;
for (RI i=1;i<=n;++i) L[a[i]]=min(L[a[i]],i),R[a[i]]=max(R[a[i]],i);
int idx=0; for (RI i=1;i<=10;++i)
for (RI j=R[i];j>=L[i];--j) ans[j]=++idx;
puts("YES");
for (RI i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
}
return 0;
}
K. XOR Dice
思博题
一个 naive 的想法就是每个骰子都放 \(0,d,d\times 2^6,d\times 2^{12},d\times 2^{18},d\times 2^{24}\),但这样会超出上界
不过后面徐神发现其实不用单纯的 shift,将某些情况组合在一起也是可以的,一种合法的方案是使用 \(0,d,d\times 2^6,d\times 2^{12},d\times (2^{6}+1),d\times (2^{12}+1)\)
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int n,d;
int main()
{
scanf("%d%d",&n,&d);
for (RI i=1;i<=n;++i)
printf("%d %d %d %d %d %d\n",0,d,d*(1<<6),d*(1<<12),d*((1<<6)+1),d*((1<<12)+1));
return 0;
}
L. (1, 2) Nim
看的博弈先 Rush 个暴力上去,打表发现以下规律:
- 当存在 \(\ge 2\) 个非 \(1\) 的堆时,先手必败;
- 当存在恰好一个非 \(1\) 的堆时,先手获胜当且仅当 \(n\bmod 3\ne 2\);
- 不存在非 \(1\) 的堆时,先手获胜当且仅当 \(n\bmod 3=1\);
证明可以用归纳法,但我只能说打表 yyds
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,x;
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%d",&n); int cnt=0;
for (RI i=1;i<=n;++i) scanf("%d",&x),cnt+=(x>1);
if (cnt>=2) puts("Grundy"); else
if (cnt==1) puts(n%3!=2?"Sprague":"Grundy");
else puts(n%3==1?"Sprague":"Grundy");
}
return 0;
M. Graphs and Colors
思路很顺畅的一个题
首先由于每种颜色要将图连通,因此至少要 \(n-1\) 条边,故 \(k>\frac{n}{2}\) 时一定无解
现在考虑对 \(k=\frac{n}{2}\) 构造一种合法方案,因为对于 \(k<\frac{n}{2}\) 的情形我们可以修改其中的一些颜色为 \(1\) 来得到一个依旧合法的解
先考虑 \(n\) 为偶数的情况,将所有点两两配对,如 \((2i-1,2i)\) 间连颜色为 \(i\) 的边
对于两个点对 \(1\le i<j<\frac{n}{2}\),在 \((2i-1,2j-1),(2i,2j)\) 间连颜色为 \(i\) 的边,在 \((2i-1,2j),(2i,2j-1)\) 间连颜色为 \(j\) 的边,手玩下会发现这样一定合法
对于 \(n\) 为奇数的情况,先求出前 \(n-1\) 个点的一组合法解,然后把 \(n\) 和每个点对用对应的颜色连通即可
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int edg[N][N];
void solve() {
int n, k; cin >> n >> k;
if (k>n/2) {
cout << "NO\n"; return ;
}
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) edg[i][j] = 0;
auto inv = [&](int a) {return n+1-a;};
for (int c=1; c<=n/2; ++c) {
edg[c][inv(c)] = c;
for (int i=1; i<c; ++i) edg[c][inv(i)] = c, edg[i][inv(c)] = c;
for (int i=c+1; i<=n/2; ++i) edg[c][i] = c, edg[inv(i)][inv(c)] = c;
}
if (n%2==1) {
for (int i=1; i<=n/2; ++i) edg[i][(n+1)/2] = i, edg[(n+1)/2][inv(i)] = i;
}
cout << "YES\n";
for (int i=2; i<=n; ++i) {
for (int j=1; j<i; ++j) {
if (edg[j][i]>k) edg[j][i] = 1;
cout << edg[j][i] << ' ';
}
cout << '\n';
}
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0);
int t; cin >> t; while (t--) solve();
return 0;
}
N. Red Black Grid
这场好多构造啊,贵国是构造王国吗.jpg
首先考虑特判 \(n\le 3\) 的情况,同时不难发现 \(k=1\or k=2\times n\times (n-1)-1\) 时一定无解
否则考虑进行一个棋盘染色,先假设整个棋盘都是 R
,然后钦定只有 \(i+j\) 为偶数的位置能把 R
翻转成 B
此时我们相当于获得了贡献为 \(2,3,4\) 的位置,要用它们拼凑出 \(k\) 的权值,讨论下会发现先用大的再用小的,配合上一些回退操作,一定可以表示出所有情况
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
const int N = 1005;
int tbl[N][N];
void solve() {
int n, k; cin >> n >> k;
int tmpk = k;
if (1==k || 2*n*(n-1) - 1 == k) {
cout << "Impossible\n";
return;
}
if (1==n) {
if (k==0)
{
cout << "Possible\n";
cout << "R\n";
} else cout << "Impossible\n";
} else if (2==n) {
if (k==0)
{
cout << "Possible\n";
cout << "RR\nRR\n";
} else
if (k==2)
{
cout << "Possible\n";
cout << "BR\nRR\n";
} else
if (k==4)
{
cout << "Possible\n";
cout << "BR\nRB\n";
} else cout << "Impossible\n";
} else if (3==n) {
if (k==0)
{
cout << "Possible\n";
cout << "RRR\nRRR\nRRR\n";
} else
if (k==2)
{
cout << "Possible\n";
cout << "BRR\nRRR\nRRR\n";
} else
if (k==3)
{
cout << "Possible\n";
cout << "RBR\nRRR\nRRR\n";
} else
if (k==4)
{
cout << "Possible\n";
cout << "RRR\nRBR\nRRR\n";
} else
if (k==5)
{
cout << "Possible\n";
cout << "RRB\nRRR\nRBR\n";
} else
if (k==6)
{
cout << "Possible\n";
cout << "RBR\nRRR\nRBR\n";
} else
if (k==7)
{
cout << "Possible\n";
cout << "RBR\nRRR\nBRB\n";
} else
if (k==8)
{
cout << "Possible\n";
cout << "BRB\nRRR\nBRB\n";
} else
if (k==9)
{
cout << "Possible\n";
cout << "RBR\nRRB\nRBR\n";
} else
if (k==10)
{
cout << "Possible\n";
cout << "BRB\nRBR\nRRB\n";
} else
if (k==12)
{
cout << "Possible\n";
cout << "BRB\nRBR\nBRB\n";
} else cout << "Impossible\n";
} else {
vector<pii> pos2, pos3, pos4;
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) if ((i+j)%2==0) {
if (i!=1 && i!=n && j!=1 && j!=n) pos4.push_back({i, j});
else {
if ((i==1 || i==n) && (j==1 || j==n)) pos2.push_back({i, j});
else pos3.push_back({i, j});
}
}
int sz2=pos2.size();
int sz3=pos3.size();
int sz4=pos4.size();
vector<pii> ans;
if (k/4 <= sz4) {
for (int i=0; i<k/4; ++i) ans.push_back(pos4[i]);
if (k%4 == 1) ans.pop_back(), ans.push_back(pos2[0]), ans.push_back(pos3[0]);
else if (k%4 == 2) ans.push_back(pos2[0]);
else if (k%4 == 3) ans.push_back(pos3[0]);
} else {
ans = pos4;
if (4*sz4+1 == k) ans.push_back({1, 2});
else {
k -= 4*sz4;
int bd = (n%2==0 ? 2 : 4);
for (int i=0; i<=bd; ++i) {
if ((k-2*i)%3==0 && (k-2*i)/3 <= sz3) {
for (int j=0; j<i; ++j) ans.push_back(pos2[j]);
for (int j=0; j<(k-2*i)/3; ++j) ans.push_back(pos3[j]);
break;
}
}
}
}
cout << "Possible\n";
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) tbl[i][j] = 0;
for (auto [r, c] : ans) tbl[r][c] = 1;
int tmp=0;
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) {
if (i-1>=1 && tbl[i-1][j] != tbl[i][j]) ++tmp;
if (i+1<=n && tbl[i+1][j] != tbl[i][j]) ++tmp;
if (j-1>=1 && tbl[i][j-1] != tbl[i][j]) ++tmp;
if (j+1<=n && tbl[i][j+1] != tbl[i][j]) ++tmp;
}
tmp /= 2;
// printf("tmp=%d\n", tmp);
assert(tmp==tmpk);
for (int i=1; i<=n; ++i) {
for (int j=1; j<=n; ++j) cout << (tbl[i][j] == 0 ? 'B' : 'R');
cout << '\n';
}
}
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0);
int t; cin >> t; while (t--) solve();
return 0;
}
Postscript
感觉今天这场啥也没写就结束了,估计是起的太早导致全程犯困,看来得好好调整下生物钟了的说