• 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;
    }
    
    
  • 相关阅读:
    unity 判断是安卓还是IOS平台
    C# set get 函数 属性访问器
    C# 字典 Dictionary
    掌握下面常用函数,学PHP不再难!
    阿里云云虚拟主机上个人网站的Https访问配置
    PHP中$_SERVER 参数详解,PHP判断当前访问的http还是https
    个人网站如何选择支付接口(API回调)
    备战NOIP——模板复习8
    备战NOIP——模板复习7
    备战NOIP——模板复习7
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/13791982.html
Copyright © 2020-2023  润新知