Ozon Tech Challenge 2020 (Div.1 + Div.2)

A
直接将两个数组从小到大排序输出。
B
题意:是给你一个括号序列,里面由若干个 ( '(' 和若干个 ) ')' ,现在让你选若干子序列,这个子序列是偶数,前一半是 ( '(' 后一半是 ) ')' ,然后问你最少需要多少操作删除若干子序列后就不存在这样的序列。
思路:
答案只能为1或者0,因为子序列是形如 ( ( ( ( ) ) ) ) (((()))) 这种情况,所以我们双指针前后扫,一个(对应一个)。然后扫完后肯定就有这样的序列了。
C
题意:
给你一个序列, a 1 , a 2 . . . . . a n a_1,a_2.....a_n ,然后让你求 1 < = i < j < = n a i a j \prod\limits_{1<=i<j<=n}^{}|a_i-a_j| ,但是由于结果可能很大,让你模一个 m m
思路:
傻了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
m m 的范围很小,1000, 所以如果 n > m n>m 的话,根据鸽笼原理,肯定会有两个 a i , a j a_i,a_j m m 同余,那么结果肯定为0,否则直接算即可。

int a[N];
int main(){
	int n = read(),m = read();
	rep(i,1,n) a[i] = read();
	if(n > m) return puts("0"),0;
	ll ans = 1;
	rep(i,1,n-1){
		rep(j,i+1,n){
			ans *= abs(a[i]-a[j]);
			ans %= m;
		}
	}
	cout <<ans;
}

D
交互题
给你一棵 n n 个节点的树,然后你可以询问 n / 2 n/2 u , v u,v 节点的LCA,然后让你找出这颗树的根节点。
思路:
我们从每次询问叶子节点 u , v u,v ,假如 r o o t = L C A ( u , v ) root=LCA(u,v) 是他们中的一个,则这个点必为根,否则,root的包含 u u 的子树,和包含 v v 的子树,都没有用了,删除掉即可。然后root的度-2,看root的度是否小于等于1,如果是,则将 r o o t root 也放进叶子节点的集合。知道剩余一个节点。

queue<int> lef;
int deg[N];
bool vis[N];
vector<int> G[N];
void dfs(int fa,int x,int root){//标记删除节点
    vis[x] = 1;
    for(int a:G[x]){
        if(a!=fa&&a!=root) dfs(x,a,root);
    }
}
int main(){
	int n = read();
	rep(i,1,n-1){
		int u = read(),v = read();
		deg[u] ++;deg[v] ++;
        G[u].push_back(v);
        G[v].push_back(u);
	}
	for(int i = 1;i <= n;++i){
		if(deg[i]== 1) {lef.push(i);}
	}
	int a,b;
    rep(i,1,n){
        if(lef.size() == 1){
            printf("! %d",lef.front());
            return 0;
        }
        while(lef.size()&&vis[lef.front()]) lef.pop();
        a = lef.front();lef.pop();
        while(lef.size()&&vis[lef.front()]) lef.pop();
        b = a;
        if(lef.size()) {b = lef.front();lef.pop();}
        if(a == b){
            printf("! %d",a);
            return 0;
        }
        printf("? %d %d\n",a,b);
        fflush(stdout);
        int root = read();
        if(root == a||root == b){
            printf("! %d",root);
            return 0;
        }
        dfs(-1,a,root);dfs(-1,b,root);//删除子树
        deg[root] -= 2;
        if(deg[root] <=1) lef.push(root);
    }
     printf("! %d",lef.front());
	
}

E
题意:
给你一个 n n ,和一个 m m ,然后让你否造一个单增的序列 a 1 , a 2 , a 3 . . . . . . a n a_1,a_2,a_3......a_n ,使得存在 a i + a j = a k a_i+a_j=a_k 的对数是 m m
思路:
首先,要知道存在 a i = i a_i=i 的序列可以使得满足题意的最多对数的序列,然后可以发现一个规律,长度为 i i 的序列,最大对数为 ( i 2 ) + ( i 4 ) + ( i 6 ) . . . . . (i-2)+(i-4)+(i-6)..... 依次递减2。很显然这是个等差数列,所以我们可以算出每一项,储存在 f f 中。
如果 f [ n ] < m f[n]<m ,则可以说明无解。
否则,我们找到小于等于 m m 最大的一项 p o s pos m m 减去 f [ p o s ] f[pos] ,然后我们遍历前面已经确定下来的 a a 数组,用 m a p map 处理 a [ i ] + a [ j ] a[i]+a[j] ,然后尝试确定其他位置的数。

int a[N];
int f[N];
map<int,int > hap;
int main(){
    int n = read(),m = read();
    rep(i,3,n){
        int d = i - 2;
        if(i&1) f[i] = (d+1)*(d+1)/4;
        else f[i] = d * d/4 + d/2;
    }
    if(f[n] < m) return puts("-1"),0;
    int i = 1;
    while(i <= n&&f[i] <= m) {a[i] = i;i ++;}
    i --;
    m -= f[i];
    if(m == 0){
        rep(j,i+1,n) a[j] = a[j-1] + 5001;
    }
    else {
        i ++;
        int M = -1;
        int pos = 1,tmp = -1;
        rep(j,1,i-2)
            rep(k,j+1,i-1) {hap[a[j]+a[k]] ++;M =max(M,a[j]+a[k]);}
        rep(j,a[i-1]+1,M){
            if(hap.count(j)&&hap[j] <= m){
                m -= hap[j];
                a[i++] = j;
                //tmp = hap[j];
            }
        }
        while(m--){
            a[i] = a[i-1] + a[i-2];
            i ++;
        }
        rep(j,i,n) a[j] = a[j-1] + 5001;
    }
    rep(j,1,n) printf("%d ",a[j]);
}

F

发布了636 篇原创文章 · 获赞 38 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_43408238/article/details/104651020