2020牛客国庆集训派对day8

2020牛客国庆集训派对day8

Easy Chess 模拟 or dfs

恶心的签到题,但是如果用dfs写就还算一个正常的题目了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
struct node{
	int x,y;
};
vector<node>ans;
bool ok;
bool vis[10][10];
void dfs(int x,int y,int sum){
	// printf("x = %d y = %d sum = %d
", x,y,sum);
	if(ok) return ;
	if(vis[8][8]&&sum) return ;
	if(sum==0){
		if(x==8&&y==8){
			int len = ans.size();
			for(int i=0;i<len;i++) printf("%c%d ", ans[i].x+'a'-1,ans[i].y);
			printf("
");
		 	ok = true;
		}
	 	return ;
	}
	for(int i=1;i<=8;i++){
		if(!vis[x][i]){
			vis[x][i] = true;
			ans.push_back(node{x,i});
			dfs(x,i,sum-1);
			ans.pop_back();
			vis[x][i] = false;
		}
		if(!vis[i][y]){
			vis[i][y] = true;
			ans.push_back(node{i,y});
			dfs(i,y,sum-1);
			ans.pop_back();
			vis[i][y] = false;
		}
	}
}

int main(){
	int n;
	scanf("%d",&n);
	ok = false;
	vis[1][1] = true;
	ans.push_back(node{1,1});
	dfs(1,1,n);
	return 0;
}

PACM Team 简单的背包+优化

这个题目一看到肯定就是想直接dp,但是这个空间不太够,又因为这个答案很小,所以可以用short优化,再而因为背包记录路径可以不同pre数组,所以节约了空间,正好开五维的大小37的dp数组

#include <bits/stdc++.h>
using namespace std;
const int maxn = 37;
short dp[maxn][maxn][maxn][maxn][maxn];
struct node{
	int p,a,c,m,g;
}e[maxn];
int res[maxn];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int p,a,c,m,g;
		scanf("%d%d%d%d%d",&p,&a,&c,&m,&g);
		e[i] = node{p,a,c,m,g};
	}
	int P,A,C,M;
	scanf("%d%d%d%d",&P,&A,&C,&M);
	int id = 0,ip = 0,ia = 0,ic = 0,im = 0,ans = 0;
	for(int i=1;i<=n;i++){
		for(int p=0;p<=P;p++){
			for(int a=0;a<=A;a++){
				for(int c=0;c<=C;c++){
					for(int m=0;m<=M;m++){
						dp[i][p][a][c][m] = dp[i-1][p][a][c][m];
						if(p>=e[i].p&&a>=e[i].a&&c>=e[i].c&&m>=e[i].m){
							if(dp[i-1][p-e[i].p][a-e[i].a][c-e[i].c][m-e[i].m]+e[i].g>dp[i][p][a][c][m]){
								dp[i][p][a][c][m] = dp[i-1][p-e[i].p][a-e[i].a][c-e[i].c][m-e[i].m]+e[i].g;
							}
						}
						if(dp[i][p][a][c][m]>ans){
							ans = dp[i][p][a][c][m];
							id = i,ip = p,ia = a,ic = c,im = m;
						}
					}
				}
			}
		}
	}
	// printf("id = %d ans = %d
", id,ans);
	int cnt = 0;
	while(id>0){
		if(dp[id][ip][ia][ic][im]==dp[id-1][ip][ia][ic][im]);
		else{
			res[++cnt] = id;
			ip-=e[id].p,ia-=e[id].a,ic-=e[id].c,im-=e[id].m;
		}
		id--;
	}
	printf("%d
", cnt);
	sort(res+1,res+1+cnt);
	for(int i=1;i<=cnt;i++) printf("%d ", res[i]-1);
	printf("
");
    return 0;
}

Easy Problemset 签到题目

把题目读懂就行了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e2+5;
typedef long long ll; 
vector<int>num[maxn];
int main(){
	int n,k;
	scanf("%d%d",&n,&k);
	ll ans = 0;
	for(int i=1;i<=n;i++){
		int x,y;
		scanf("%d",&x);
		while(x--){
			scanf("%d",&y);
			num[i].push_back(y);
		}
	}
	for(int j=0;j<maxn;j++){
		for(int i=1;i<=n;i++){
			if(num[i].size()<=j) ans+=50,k--;
			else {
				if(num[i][j]>=ans) ans+=num[i][j],k--;
			}
			if(!k) break;
			// printf("i = %d k = %d now = %d ans = %lld
", i,j,num[i][j],ans);
		}
		if(!k) break;
	}
	printf("%lld
", ans);
	return 0;
}

Diff-prime Pairs 枚举

枚举每一个数,他可能都是那个底数gcd,所以就求他的质数倍,有多少个质数倍数小于等于n

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e7+5;
typedef long long ll; 
int pri[maxn],cnt,v[maxn];
void init() {
    cnt = 0;
    memset(v,0,sizeof(v));
    for (int i = 2; i < maxn; ++i) {
        if (!v[i]) {
            v[i] = i;
            pri[cnt++] = i;
        }
        for (int j = 0; j < cnt; ++j) {
            if (1ll * i * pri[j] >= maxn) break;
            v[i * pri[j]] = pri[j];
        }
    }
}


int main(){
	int n;
	init();
	scanf("%d",&n);
	ll ans = 0;
	for(int i=1;i<=n;i++){
		ll cur = 0;
		for(int j=0;j<cnt;j++){
			if(1ll*i*pri[j]>n) break;
			cur++;
		}
		ans+=cur*(cur-1);
	}
	printf("%lld
", ans);
}

Geometry Challenge splay

虽然说是splay模板题,但是完全想不到用splay来维护。

为什么可以用splay来维护呢,这个是因为对于这个序列我可把他转化成splay的中序遍历,用splay来维护这个中序遍历。

举例:12345,p=3,s=2

那么首先想让2变成splay的根节点,那么根节点左右两边的子树的中序遍历不会改变,因为要12放到34后面,所以此时的根节点2的右子树设为空,并且令2的右儿子变成根节点,所以现在就变成两棵树了,一棵树是只有12,一棵树有345,然后找到345这棵树的第2大的数4,把4变成根节点,此时12查到34后面5前面,所以让4的原来的右儿子5变成12这棵树的右儿子,12这棵树变成4的右儿子即可。

主要就是要理解splay维护序列的中序遍历。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
/*
splay 模板
支持: 
1 插入节点   
2 删除节点
3 求小于一个数的最大值
4 求大于一个数的最小值
5 求一个数处于存在的第几个位置
6 求第k个位置的值是多少
。。。
*/
struct Splay{
    int fa[maxn],key[maxn],sub_size[maxn],sons[maxn][2],recy[maxn],root,whole_size;
    void init(){
        root = 0,whole_size = 0;
    }
    //清空一个节点
    inline void clear(int x){
        fa[x] = key[x] = recy[x] = sons[x][0] = sons[x][1] = sub_size[x] = 0;
    }

    int getWhich(int x){
        return sons[fa[x]][1] == x;
    }

    inline void Update(int x){
        if(x){
            sub_size[x] = recy[x];
            if(sons[x][0]) sub_size[x]+=sub_size[sons[x][0]];
            if(sons[x][1]) sub_size[x]+=sub_size[sons[x][1]];
            //先更新儿子节点,再更新父亲节点
        }
    }

    void connect(int x,int y,int z){//x 连到 y 下面,关系是 z
        if(y) sons[y][z] = x;  // y存在,则y的z儿子是x
        if(x) fa[x] = y; //x 存在,则x的父亲是y
    }

    void rotate(int x){
        int father = fa[x],ffather = fa[father],m = getWhich(x),n = getWhich(father);
        connect(sons[x][m^1],father,m);
        connect(father,x,m^1);
        connect(x,ffather,n);
        Update(father),Update(x);
    }

    void splay(int x){
        for(int father;father = fa[x];rotate(x)){
            //splay 的双旋操作,还有点不太理解
            if(fa[father]) rotate(getWhich(x)==getWhich(father)?father:x);
        }
        root = x;
    }

    void Insert(int x){
        // printf("INSETR
");
        if(!root){
            root = ++whole_size;
            key[root] = x;
            recy[root] = sub_size[root] = 1;
            sons[root][0] = sons[root][1] = 0;
            return;
        }
        int now = root, father = 0;
        while(true){
            if(key[now]==x){
                recy[now]++;
                Update(now),Update(father);
                splay(now);
                return ;
            }
            father = now, now = sons[now][x>key[now]];
            if(!now){
                ++whole_size;
                key[whole_size] = x,fa[whole_size]=father;
                recy[whole_size] = sub_size[whole_size] = 1;
                sons[father][x>key[father]] = whole_size;
                // printf("sons[%d][%d]=%d
", father,x>key[father],whole_size);
                Update(father),splay(whole_size);
                return ;
            }
        }
    }
    int findRank(int x){
        int now = root,ans = 0;
        while(true){
            if(x<key[now]){
                now = sons[now][0];
                continue;
            }
            ans+=sub_size[sons[now][0]];
            if(x==key[now]){
                splay(now);
                return ans+1;
            }
            ans += recy[now];
            now = sons[now][1];
        }
    }
    int findKth(int x){
        int now = root;
        while(true){
            if(sons[now][0] && x<=sub_size[sons[now][0]]){
                now = sons[now][0];
                continue;
            }
            if(sons[now][0]) x-= sub_size[sons[now][0]];
            if(x<=recy[now]) {
                splay(now);
                return key[now];
            }
            x-=recy[now];
            now = sons[now][1];
        }
    }
    //找到当前根节点的左子树中最大的节点
    //其实就是找到小于当前根节点的最大值
    int findPre(){
        int now = sons[root][0];
        while(sons[now][1]) now = sons[now][1];
        return now;
    }
    //找打大于当前根节点的最小值
    int findSuffix(){
        int now = sons[root][1];
        // printf("ss now=%d
", now);
        while(sons[now][0]) now = sons[now][0];
        return now;
    }

    void Delete(int x){
        findRank(x);//主要为了把x这个值用findRank函数找到,然后旋转到根节点变成root,以便于后续的操作
        if(recy[root]>1){
            recy[root]--;
            Update(root);
            return ;
        }
        if(!sons[root][0]&&!sons[root][1]){
            clear(root);
            root = 0;
            return ;
        }
        if(!sons[root][0]){
            int old_root = root;
            fa[root = sons[root][1]] = 0;
            clear(old_root);
            return ;
        }
        if(!sons[root][1]){
            int old_root = root;
            fa[root = sons[root][0]] = 0;
            clear(old_root);
            return ;
        }
        int old_root = root, left = findPre();
        splay(left);
        connect(sons[old_root][1],root,1);
        clear(old_root);
        Update(root);
    }

    void debug(int u,int pre,int z){
        printf("u=%d pre=%d z=%d
",u,pre,z);
        if(sons[u][0]) debug(sons[u][0],u,0);
        if(sons[u][1]) debug(sons[u][1],u,1);
    }
}splay;


int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    splay.init();
    for(int i=1;i<=n;i++) splay.Insert(i);
    while(m--){
        int p,s;
        scanf("%d%d",&p,&s);
        if(p==1) continue;
        int x = splay.findKth(p);
        int u = splay.findPre();

        splay.splay(u);
        // printf("u = %d x = %d
", u,x);
        // splay.debug(splay.root,0,0);
        // printf("ssss
");

        int v = splay.sons[u][1];
        splay.fa[v] = 0,splay.root = v;


        int now = splay.findKth(s);
        int cur = splay.sons[now][1];
        splay.sons[now][1] = u;
        splay.fa[u] = now;
        splay.sons[u][1] = cur;
        splay.fa[cur] = u;
        splay.Update(u);
        splay.Update(now);

        // splay.debug(splay.root,0,0);
        // printf("ssss
"); 
    }
    for(int i=1;i<=n;i++) {
        printf("%d", splay.findKth(i));
        if(i==n) printf("
");
        else printf(" ");
    }
    return 0;
}

Landscape Improved 思维+二分答案

题解:

二分一个数x,那么枚举每一个位置,对于位置 i ,他如果要成为一个边界,那么他可以对 之后的 i + x - a[i] 位置做贡献,可以对 i - x + a[i] 之前的位置做贡献。

这个做贡献指的是,比如 r,那么r 之后的数要砌 x 这么高的高度,那么在往前放格子,放到 i 这个位置之后,前面的就不需要放格子了。

#include <bits/stdc++.h>
#define lc (id<<1)
#define rc (id<<1|1)
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
int a[maxn],pre[maxn],last[maxn],el[maxn];
ll n,m,sum[maxn];
bool check(ll x){
    for(int i=0;i<=n;i++) pre[i] = 0,last[i] = inf;
    for(int i = 1;i <= n; i++){
        int r = i + x - a[i];
        if(r<=n) pre[max(r,i)] = i;
    }
    for(int i=n;i>=1;i--){
        int l = i - x + a[i];
        if(l>=1) last[min(l,i)] = i;
    }
    int l = 0;
    for(int i=1;i<=n;i++){
        l = max(l,pre[i]);
        el[i] = l;
    }
    int r = inf;
    for(int i=n;i>=1;i--){
        r = min(r,last[i]);
        l = el[i];
        if(r>n||l<1) continue;
        if(l == r) return true;
        ll all = sum[r-1]-sum[l];
        ll len1 = i - l - 1,len2 = r - i - 1;
        ll cost = (x+x-len1)*(len1+1)/2+(x+x-len2)*(len2+1)/2-x;
        if(cost-all<=m) return true;
    }
    return false;
}

int main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        sum[i] = sum[i-1]+a[i];
    }
    int l = 0,r = inf,ans = l;
    while(l<=r){
        int mid = (l+r)>>1;
        if(check(mid)) ans = mid,l = mid+1;
        else r = mid-1;
    }
    printf("%d
", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/EchoZQN/p/13791982.html