Codeforces Round #720 (Div. 2)
Codeforces Round #720 (Div. 2)
来源:https://codeforces.com/contest/1521
A.Nastia and Nearly Good Numbers
若 $\mathrm{B=1}$, 则不合法.
否则直接构建 $\mathrm{A, A \times B, A \times (B+1)}$.
由于 $\mathrm{B}$ 与 $\mathrm{B+1}$ 肯定互质,所以是对的.
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
void solve() {
ll A,B;
scanf("%lld%lld",&A,&B);
if(B==1) {
printf("NO\n");
}
else {
printf("YES\n");
printf("%lld %lld %lld\n",A*B, A, (B+1)*A);
}
}
int main() {
// setIO("input");
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
B.Nastia and a Good Array
对于 $\mathrm{1}$ ~ $\mathrm{(n-1)}$, 可以每次将 $\mathrm{a[i]}$ 构建成一个远大于 $10^9$ 的数.
只要保证相邻两数差为 $1$ 就肯定互质.
但是最后一个数和倒数第二个数不一定互质.
于是我们可以先找到一个远大于 $\mathrm{10^9}$ 的质数,然后让倒数第二个数等于这个质数.
前面的数挨个推就行了.
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200009
#define ll long long
#define pb push_back
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int a[N], PR;
void solve() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
}
if(n==1) {
printf("0\n");
return ;
}
printf("%d\n",n-1);
int con=PR-(n-2);
for(int i=2;i<=n;++i) {
// a[i]->x a[j]->y
printf("%d %d %d %d\n",i-1,i,con,min(a[i],a[i-1]));
a[i]=min(a[i], a[i-1]);
a[i-1]=con;
++con;
}
// printf("%d %d %d %d\n",n-1,n,a[n-1],con);
// a[n]=con;
// for(int i=1;i<=n;++i) printf("%d\n",a[i]);
}
void init() {
PR=(int)1e9+(int)1e8;
for( ; ; ++PR) {
int flag=0;
for(int j=2;j*j<=PR;++j) {
if(PR%j==0) { flag=1; break; }
}
if(!flag) break;
}
}
int main() {
// setIO("input");
int T;
init();
scanf("%d",&T);
while(T--) solve();
return 0;
}
C.Nastia and a Hidden Permutation
首先,给的这个取 $\mathrm{min,max}$ 的东西不好办.
想把 $\mathrm{min,max}$ 消掉就需要其中有元素是无穷大或无穷小.
若知道 $\mathrm{n}$ 的位置,则其他部分都可以直接 $O(1)$ 求.
关键就是在于怎么求 $\mathrm{n}$ 的位置.
不妨用 $1$ 操作来确定,可以得到不超过 $5$ 个可能为 $\mathrm{n}$ 的位置, 然后再暴力找.
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 100004
#define ll long long
#define pb push_back
using namespace std;
vector<int>v;
int ans[N];
int query(int t, int i, int j, int x) {
printf("? %d %d %d %d\n",t,i,j,x);
printf("\n");
fflush(stdout);
int fin;
scanf("%d",&fin);
return fin ;
}
void solve() {
int n , mx=0;
scanf("%d",&n), v.clear();
for(int i=1;i+1<=n;i+=2) {
// [i, i+1]
int p = query(1, i, i + 1, n - 1);
if(p == n) {
mx = i + 1;
break ;
}
else {
if(p == n - 1) {
v.pb(i);
v.pb(i+1);
}
}
}
if(!mx) {
// 还未查询到最大值.
if(n & 1) v.pb(n);
// v: 可能是最大值.
for(int i=0;i<v.size();++i) {
if(mx) break;
for(int j=0;j<v.size();++j) {
if(i == j) continue;
int p = query(1, v[j], v[i], n - 1);
if(p == n) {
mx = v[i];
break;
}
}
if(mx) break;
}
}
ans[mx] = n;
// 查询到了最大值位置.
int u=0, v=0;
for(int i=1;i<=n;++i) {
if(i == mx) continue;
ans[i] = query(2, mx, i, 1);
if(ans[i] <= 2) {
ans[i] = 0;
if(!u) u=i;
else v=i;
}
}
// (u, v) 不确定谁是 1 或 2.
int p = query(1, u, v, 1);
if(p == 1) ans[v]=1, ans[u]=2;
else ans[v]=2, ans[u]=1;
printf("! ");
for(int i=1;i<=n;++i) {
printf("%d ",ans[i]);
}
printf("\n");
fflush(stdout);
}
int main() {
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
D.Nastia Plays with a Tree
先考虑怎么求最小步数,然后再考虑如何输出方案.
我们发现,若想保证图联通,则删边次数一定等于加边次数.
显然,这个加边顺序是无所谓的,不妨统一先删边后加边.
一条链的子图一定也是一条链,所以删掉边后这个树就变成了若干条链.
那么就令 $\mathrm{f[x],g[x]}$ 分别表示 $\mathrm{x}$ 子树被分成若干条链且是否延申的极小值.
这个可以用 DP 来求.
然后输出方案的话就再 DFS 一遍,记录一下做右端点即可.
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 100009
#define ll long long
#define pb push_back
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
vector<int>G[N],fv[N],gv[N];
int n,f[N],g[N],fa[N], del[N];
struct data {
int x,val;
data(int x=0,int val=0):x(x),val(val){}
};
vector<data>h;
bool cmp(data i, data j) {
return i.val<j.val;
}
void dfs(int x, int ff) {
fa[x]=ff;
for(int i=0;i<G[x].size();++i) {
int v=G[x][i];
if(v==ff) continue;
dfs(v, x);
}
int sum=0;
h.clear();
for(int i=0;i<G[x].size();++i) {
int v=G[x][i];
if(v==ff) continue;
sum+=(f[v]+1);
h.pb(data(v, g[v]-(f[v]+1)));
}
// 求 g[x]
g[x]=sum;
sort(h.begin(), h.end(), cmp);
if(h.size()) {
g[x]=min(g[x], sum + h[0].val);
}
// 处理 gv
if(g[x]!=sum) gv[x].pb(h[0].x);
// 求 f[x]
f[x]=g[x];
if(h.size() > 1) {
f[x]=min(f[x], sum+h[0].val+h[1].val);
}
// 处理 fv
if(f[x]==g[x]) {
for(int i=0;i<gv[x].size();++i)
fv[x].pb(gv[x][i]);
}
else {
fv[x].pb(h[0].x);
fv[x].pb(h[1].x);
}
}
int scc, tot;
struct P{
int x,y;
P(int x=0,int y=0):x(x),y(y){}
}prr[N], qrr[N];
// ty=1: f[x]
// ty=2: g[x]
int dfs2(int x, int ty, int is) {
if(ty==1&&f[x]==g[x]) ty=2;
if(ty==1) {
int c[2];
for(int i=0;i<fv[x].size();++i) {
int v=fv[x][i];
c[i] = dfs2(v, 2, 1);
}
for(int i=0;i<G[x].size();++i) {
int v=G[x][i];
if(v==fa[x]||v==fv[x][0]||v==fv[x][1]) continue;
del[v]=1, dfs2(v, 1, 0);
}
++scc;
prr[scc].x=c[0];
prr[scc].y=c[1];
}
else {
if(!gv[x].size()) {
if(is == 0) {
++scc;
prr[scc].x=x;
prr[scc].y=x;
}
for(int i=0;i<G[x].size();++i) {
int v=G[x][i];
if(v==fa[x]) continue;
del[v]=1, dfs2(v, 1, 0);
}
return x;
}
else {
int p = gv[x][0];
int q = dfs2(p, 2, 1);
for(int i=0;i<G[x].size();++i) {
int v=G[x][i];
if(v==fa[x]||v==p) continue;
del[v]=1, dfs2(v, 1, 0);
}
if(is == 0) {
++scc;
prr[scc].x=x;
prr[scc].y=q;
}
return q;
}
}
}
void solve() {
scanf("%d",&n);
for(int i=1;i<n;++i) {
int x, y;
scanf("%d%d",&x,&y);
G[x].pb(y);
G[y].pb(x);
}
dfs(1, 0);
printf("%d\n",f[1]);
dfs2(1, 1, 0);
for(int i=1;i<=n;++i) {
if(del[i]) qrr[++tot]=P(fa[i], i);
}
for(int i=1;i<=f[1];++i) {
printf("%d %d %d %d\n",qrr[i].x, qrr[i].y, prr[i].y, prr[i+1].x);
}
for(int i=0;i<=n;++i) {
fa[i]=f[i]=g[i]=0;
gv[i].clear();
fv[i].clear();
del[i]=0;
G[i].clear();
}
tot=scc=0;
}
int main() {
// setIO("input");
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
E.Nastia and a Beautiful Matrix
先二分矩阵的长度 $\mathrm{n}$, 然后将矩阵向下图这样进行 3 种颜色的染色.

如果染色的位置足够且出现次数最多的数可以被加入进去,则一定合法.
加数的时候优先加入红,然后加入蓝,最后加黄.
不合法的情况要求后面加入的数的出现次数要大于众数,所以只要众数能加入就合法.
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define N 200009
#define ll long long
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int A[N],m,k;
struct node {
int x,y;
node(int x=0,int y=0):x(x),y(y){}
}C[N],B[N],aa[N];
int tc,tb,ta,val[N], ma[1000][1000];
int check(int len) {
int total=len*len-(len/2)*(len/2);
if(total < m) return 0;
int c1=len*((len+1)/2);
if(c1 < A[1]) return 0;
return 1;
}
void solve() {
int mx=0;
tc=tb=ta=0;
scanf("%d%d",&m,&k);
for(int i=1;i<=k;++i) {
scanf("%d",&A[i]);
val[i]=i;
if(A[i]>A[mx]) mx=i;
}
swap(A[mx],A[1]);
swap(val[mx], val[1]);
// A[1]: 最大的.
int len = 1;
for(len=1; ; ++ len) {
if(check(len)) {
break ;
}
}
// 得到长度.
for(int i=1;i<=len;i+=2) {
for(int j=1;j<=len;j+=2) {
C[++tc]=node(i, j);
}
}
for(int i=1;i<=len;i+=2) {
for(int j=2;j<=len;j+=2) {
B[++tb]=node(i, j);
}
}
for(int i=2;i<=len;i+=2) {
for(int j=1;j<=len;j+=2) {
aa[++ta]=node(i, j);
}
}
for(int i=1;i<=len;++i) for(int j=1;j<=len;++j) ma[i][j]=0;
for(int i=1;i<=k;++i) {
for(int j=1;j<=A[i];++j) {
if(ta) {
ma[aa[ta].x][aa[ta].y] = val[i];
--ta;
continue;
}
if(tc) {
ma[C[tc].x][C[tc].y] = val[i];
--tc;
continue;
}
if(tb) {
ma[B[tb].x][B[tb].y] = val[i];
--tb;
continue;
}
}
}
printf("%d\n",len);
for(int i=1;i<=len;++i) {
for(int j=1;j<=len;++j) {
printf("%d ",ma[i][j]);
}
printf("\n");
}
}
int main() {
// setIO("input");
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号