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 种颜色的染色. 

Codeforces Round #720 (Div. 2)_#include

如果染色的位置足够且出现次数最多的数可以被加入进去,则一定合法.   

加数的时候优先加入红,然后加入蓝,最后加黄.    

不合法的情况要求后面加入的数的出现次数要大于众数,所以只要众数能加入就合法.   

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