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; }