zoukankan      html  css  js  c++  java
  • 2017 NOIp提高组 DAY2 试做

    衔接 ( ext{DAY1})->(2017) ( ext{NOIp}) 提高组 ( ext{DAY1}) 试做

    D2T1 奶酪

    题库原题,很裸的并查集,就是建边相对复杂一些

    D2T2 宝藏

    题目描述

    下方传送门
    题目链接
    上方传送门

    思路分析

    • 刚做的时候太着急了,上去就试着糊了个 ( ext{BFS}),然后 ( ext{get})(40pts)

    • 后来看一眼数据范围,(n) 才这么小,直接暴搜,贪心和剪枝都不需要,直接枚举所有情况(全排列)就行,喜提 (70pts)

    • 那要想 (A) 掉这道题,显然如果还是直接暴搜是过不了 (12) 这个点的(不过好像有大佬暴搜剪枝给过了),但是这样做至少正确性是有保证的,所以我们考虑如何能更快一些

    • 再看一眼数据范围,正如林sir所说:

      这数据范围不状压吗,疯狂暗示

      再如学长说的:

      ——你们不会有人枚举全排列直接暴搜吧?
      ——那用啥?
      ——状压呀。

    • 所以直接用状压再优化一下就好了,小 (DP)

    Code

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #define N 20
    using namespace std;
    inline int read(){
    	int x = 0,f = 1;
    	char ch = getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*f;
    }
    const int inf = 0x3f3f3f3f;
    int n,m,e[N][N],dis[N],dp[1<<N],ans=inf;
    bool flag[N][N];
    void dfs(int x){
    	for(int i = 1;i <= n;i++){ //枚举未访问的点是由哪一个点转移过来的即可
    		if(1<<(i-1)&x){
    			for(int j = 1;j <= n;j++){
    				if(!(1<<(j-1)&x)&&flag[i][j]){
    					if(dp[1<<(j-1)|x]>dp[x]+dis[i]*e[i][j]){//
    						int pro = dis[j];
    						dis[j] = dis[i]+1;
    						dp[1<<(j-1)|x]=dp[x]+dis[i]*e[i][j];
    						dfs(1<<(j-1)|x);
    						dis[j] = pro; //搜完记得回溯
    					}
    				}
    			}
    		}
    	}
    }
    int main(){
    	memset(e,0x3f,sizeof(e));
    	n = read(),m = read();
    	for(int i = 1;i <= m;i++){
    		int u,v,w;u = read(),v = read(),w = read();
    		e[u][v] = e[v][u] = min(e[u][v],w);
    		flag[u][v] = flag[v][u] = 1;
    	}
    	for(int i = 1;i <= 12;i++){
    		memset(dis,0x3f,sizeof(dis));
    		memset(dp,0x3f,sizeof(dp));
    		dis[i] = 1;
    		dp[1<<(i-1)] = 0;
    		dfs(1<<(i-1));
    		ans = min(ans,dp[(1<<n)-1]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    D2T3

    题目描述

    下方传送门
    题目链接
    上方传送门

    思路分析

    • 暴力,30分,然后就不会了
    • 当然还是要有钻研精神的,于是继续边看题解边
    • 对于行来说,每一行都有机会移动,然而对于列来说,移动的只有最后一列
    • 每次移动,其实都相当于是一个删除操作再加上一个插入操作,所以可以用平衡树或者动态开点的线段树来维护(树状数组也彳亍,但我不会)
    • 然后将行和列分开建树,每一行建一个,列只建最后一列就行了,然后考虑各种操作
      • 查询和删除:,这两个是同步进行的,因为每次查询都对应一次删除。用线段树记录相应区间的删除个数,此时区间内的点数就是区间大小减去删除的。然后接下来考虑再删的时候,和查 (K) 大相似如果要删的数小于区间点数,就去左子树删,否则去右子树删
      • 插入:开一个vector将所有插入的数装下就好
      • 查询答案:
        • 移动列,查询这一列线段树的第 (x) 个的位置 (pos),如果 (pos<=n) 说明是原来的移上去的,答案就是 (pos*m)。否则就是后来插进去的,输出 (vector) 里相应的元素即可
        • 移动行,和上面的大同小异,行列颠倒即可,注意维护行的线段树内原有的节点是每一列的前 (m-1) 个,不包含最后一列

    Code

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #define N 300010
    #define int long long
    #define R register
    using namespace std;
    inline int read(){
    	int x = 0,f = 1;
    	char ch = getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*f;
    }
    int n,m,q,sum[N*20],lch[N*20],rch[N*20],rt[N],mx,pos,cnt;
    vector<int>vc[N];
    void modify(int &u,int l,int r){ //更新删除的点
    	if(!u)u = ++cnt;
    	sum[u]++;
    	if(l==r)return;
    	int mid = (l+r)>>1;
    	if(pos<=mid)modify(lch[u],l,mid);
    	else modify(rch[u],mid+1,r);
    }
    int query(int u,int l,int r,int w){ //查询位置
    	if(l==r)return l;
    	int mid = (l+r)>>1;
    	int tmp = mid-l+1-sum[lch[u]];
    	if(w<=tmp)return query(lch[u],l,mid,w);
    	else return query(rch[u],mid+1,r,w-tmp);
    }
    int get_ans(int x,int y){ //移动列
    	pos = query(rt[n+1],1,mx,x);
    	modify(rt[n+1],1,mx);
    	int res = pos<=n ? pos*m : vc[n+1][pos-n-1];
    	vc[n+1].push_back(y?y:res);
    	return res;
    }
    int get_ans1(int x,int y){ //移动行
    	pos = query(rt[x],1,mx,y);
    	modify(rt[x],1,mx);
    	int res = pos<m? (x-1)*m+pos : vc[x][pos-m];
    	vc[x].push_back(get_ans(x,res)); //再移一下列
    	return res;
    }
    signed main(){
    	n = read(),m = read(),q = read();
    	mx = max(n,m)+q;
    	for(int i =1;i <= q;i++){
    		int x,y;x = read(),y = read();
    		printf("%lld
    ",y==m ? get_ans(x,0) : get_ans1(x,y));
    	}
    	return 0;
    }
    

    总结

    • (T1) 并查集还算是比较水的
    • (T2) 暴力分还是很足,当然正解也就是优化一下,并无太大本质区别
    • (T3) 就有点ex了,30分有手就行,正解相当不友好,虽然线段树的代码很短但是不太好想,毕竟压轴题
    • 再结合一下 (D1) 的,不翻车的话 (300) 分以上应该没啥问题, (T1)(A) 掉的话在适当拿一些暴力即可,发挥好可以到 (400) 分左右,不过可能存在误差,毕竟环境不太一样

    附洛谷对应题单->2017 NOIp

  • 相关阅读:
    intel 蓝牙驱动安装时报错
    H310C,B365,M.2 NVME SSD,USB3.0,安装 WIN7 64 位
    C# .NET 判断输入的字符串是否只包含数字和英文字母
    squid http,https, 代理,默认端口3128
    C# .net mvc web api 返回 json 内容,过滤值为null的属性
    centos7安装python-3.5
    systemctl命令完全指南
    Centos7中systemctl命令详解
    Python if 和 for 的多种写法
    CentOS 7.0,启用iptables防火墙
  • 原文地址:https://www.cnblogs.com/hhhhalo/p/13515483.html
Copyright © 2011-2022 走看看