zoukankan      html  css  js  c++  java
  • Codeforces Round #703 (Div. 2) A~E题解

    本场链接:Codeforces Round #703 (Div. 2)

    闲话

    感觉CF就没打的好的日子,总是差那么一点就做完了,蛮破坏心情的.CD都是差一点就拐过弯来了.先把A~D写了,E还在施工,下午可能还有事估计得丢晚上再更新E.

    A. Shifting Stacks

    (n)堆书,每堆书的高度是(h_i).你可以从(i)位置抽若干本书堆到(i+1)列上.操作次数不限,问你能不能把所有书的高度顺次做成一个严格上升序列.

    数据范围:

    (1 leq n leq 100)

    思路

    数据范围很小,而且限制比较强:你只能从(i)移动到(i+1).所以不妨这样暴力地把所有的书往后面推:第一摞书如果不是(0)就把所有书全部挪到第二堆上,第二堆书在不少于第一堆书的前提下全部往第三堆书上挪,看她最后是不是一个严格上升序列就可以了.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 105;
    ll a[N];
    
    int main() 
    {
    	int T;scanf("%d",&T);
    	while(T--)
    	{
    		int n;scanf("%d",&n);
    		forn(i,1,n)	scanf("%lld",&a[i]);
    		bool ok = 1;
    		if(a[1] != 0)
    		{
    			a[2] += a[1];
    			a[1] = 0;
    		}
    		forn(i,2,n)
    		{
    			if(a[i] <= a[i - 1])	ok = 0;
    			a[i + 1] += a[i] - a[i - 1] - 1;
    			a[i] = a[i - 1] + 1;
    		}
    		if(!ok)	puts("NO");
    		else puts("YES");
    	}
    	return 0;
    }
    

    B. Eastern Exhibition

    把城市地图看做是一个二维平面,有(n)个居民住在((x_i,y_i))位置上.现在要建立一个博物馆,要求任何人到博物馆的距离(dist = |x-tx| + |y - ty|)相同,其中((tx,ty))表示博物馆的位置.

    数据范围:

    (1 leq n leq 1000)

    (0 leq x_i,y_i leq 10^9)

    思路

    二维问题分割成两个一维问题:在(x)轴上有多少个位置选定之后,保证所有人的(x)坐标到此绝对值都相同且最小,这个就是货仓选址那个题的经典结论:取所有(x)的中位数,如果(n)是奇数那么中位数唯一;反之可以选中间两个数之间的任何一个位置.那么由于两个子问题相互独立,直接乘法原理统计方案数就可以了.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    typedef pair<int,int> pii;
    #define x first
    #define y second
    
    const int N = 1005;
    int x[N],y[N];
    int main() 
    {
    	int T;scanf("%d",&T);
    	while(T--)
    	{
    		int n;scanf("%d",&n);	
    		forn(i,1,n)	scanf("%d%d",&x[i],&y[i]);
    		if(n & 1)	puts("1");
    		else
    		{
    			sort(x + 1,x + n + 1);
    			sort(y + 1,y + n + 1);
    			int cx = x[n / 2 + 1] - x[n / 2],cy = y[n / 2 + 1] - y[n / 2];
    			printf("%lld
    ",(1ll * cx + 1) * (cy + 1));
    		}
    	}
    	return 0;
    }
    

    C. Guessing the Greatest

    交互

    给定一个(n)有一个长度为(n)的数组.每次可以发起一个询问([l,r]),返回在([l,r])之内次大值的下标.至多询问40/20次,要求得出最大值的下标.

    数据范围:

    (2 leq n leq 10^5)

    所有元素不同

    思路

    询问(20)次,很近(log_2^n).考虑构造一个二分框架:([l,r])区间,以及一个中点(mid),如果发起对([l,mid])的询问,是否能正确的移动指针.

    很快会发现直接去搞一个二分没什么信息.但是可以这么先来一次:询问出([1,n])里次大值的位置(p).接着可以想到一个方向:假如([1,p])的询问结果是(p)的话,那么最大值一定在([1,p])这个区间内.不过有个例外情况:(p=1)时是不可能的,需要排除.

    如果最大值在([1,p])这个区间内,那么现在的问题可以转换成:找最后一个位置(k)满足([k,p]=p),这个二分就可以了.

    反过来如果最大值在([p,n])这个区间内,那么同样可以转换成:找第一个位置(k)满足([p,k]=p),同样跑一个二分.

    这里其实还有一种想法:把(p)确定之后每次踢掉另外一半的数组,然后去看剩下那部分数组,构成一个范围更小的子问题.但是不能保证查询次数.这样做二分是肯定的.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    typedef pair<int,int> pii;
    #define x first
    #define y second
    
    map<pii,int> cache;
    int query(int l,int r)
    {
    	if(l >= r)	return -1;
    	pii q = {l,r};
    	if(cache.count(q))	return cache[q];
    	cout << "? " << l << " " << r << endl;
    	int res;cin >> res;
    	return res;
    }
    
    int main() 
    {
    	ios::sync_with_stdio(0);cin.tie(0);
    	int n;cin >> n;
    	int l = 1,r = n;
    	cout << "? " << "1 " << n << endl;
    	int p;cin >> p;
    	if(p > 1 && query(1,p) == p)
    	{
    		int l = 1,r = p;
    		while(l < r)
    		{
    			int mid = l + r + 1 >> 1;
    			if(query(mid,p) == p)	l = mid;
    			else r = mid - 1;
    		}
    		cout << "! " << l << endl;
    	}
    	else
    	{
    		int l = p,r = n;
    		while(l < r)
    		{
    			int mid = l + r >> 1;
    			if(query(p,mid) == p)	r = mid;
    			else l = mid + 1;
    		}
    		cout << "! " << l << endl;
    	}
    	return 0;
    }
    

    D. Max Median

    给定一个长度为(n)的数组(a),求一个长度至少为(k)的连续子段,要求中位数最大值.

    在本题中,中位数只取((n+1)/2(↓)).

    数据范围:

    (1 leq k leq n leq 2 * 10^5)

    (1 leq a_i leq n)

    思路

    首先贪心肯定死,考虑dp.但是dp必须要有一个性质:对于以(i)为末尾的段,中位数最大只在长度固定的段取到.但是着很显然不显示.那么只剩一种做法了:直接枚举所有可能的答案,并且检查答案是否合法.枚举部分可以考虑使用二分加速,也是一个比较套路的手段了:二分是合理的原因在于对于一个合理的答案(x),即中位数至少能是(x)的前提下,如果把(x)变小也是成立的,这个性质具有单调性.

    直接构造一个二分就可以了,问题剩下一个如何检查中位数是否至少能是(x).问题可以转换成判定:是否存在一个长度至少是(k)的连续子段,满足(geq x)的数的个数严格大于(<x)的数的个数.那么这个就是一个比较套路的东西了,每次可以构造一个数组(s[i] = a[i] >= x ? 1 : -1).并且做一个前缀和.如果严格大于,那么就说明和严格大于(0).问题就转换成了与(0)的大小关系.形式来说:找一个(igeq k)以及一个(j in [0,i-k]).满足(s[i] - s[j] > 0).那么贪心(s[j])只用前面的最小值,维护前缀最小值就可以判断了.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 2e5+7;
    int a[N],s[N],minv[N];
    int n,k;
    
    bool check(int x)
    {
    	forn(i,1,n)	s[i] = s[i - 1] + (a[i] >= x ? 1 : -1);
    	forn(i,1,n)	minv[i] = min(minv[i - 1],s[i]);
    	forn(i,k,n)	if(s[i] - minv[i - k] > 0)	return 1;
    	return 0;
    }
    int main() 
    {
    	scanf("%d%d",&n,&k);
    	forn(i,1,n)	scanf("%d",&a[i]);
    	
    	int l = 1,r = n;
    	while(l < r)
    	{
    		int mid = l + r + 1 >> 1;
    		if(check(mid))	l = mid;
    		else r = mid - 1;
    	}
    	
    	printf("%d",l);
    	return 0;
    }
    

    E. Paired Payment

    给定一个(n)(m)边无向带边权图.每次移动至少移动两条边,例如有(a-b-c)则一次移动是直接从(a->c),产生的代价是((w_{ab} + w_{bc})^2).求(1)到所有点的最短距离,如果不能走到输出-1.

    数据范围:

    (1 leq n leq 10^5)

    (1 leq m leq 2 * 10^5)

    (1 leq w_i leq 50)

    思路

    最暴力的做法就是直接从(u)搜所有点(v),再从(v)搜所有点(z)跑单源最短路.考虑如何优化:对于任何一个点(v)如果以他作为中转点,也就是(u-v-z)这样走,那么从(v)走进来的所有边,可以把所有非最小值的踢掉.因为不是最小值的走到(z)不如用最小值的走过去,对所有的点维护一个入边的最小值就可以了.

    这个玩意能跑过去的原因是权值的范围非常小,最多不过引发50次额外的更新.也不知道写点什么,就挺神奇的.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    typedef pair<int,int> pii;
    #define x first
    #define y second
    
    const int N = 1e5+7,M = 4 * N;
    int edge[M],succ[M],ver[N],cost[M],idx;
    int dist[N],mn[N];
    bool st[N];
    
    void add(int u,int v,int w)
    {
    	edge[idx] = v;
    	cost[idx] = w;
    	succ[idx] = ver[u];
    	ver[u] = idx++;
    }
    
    void dijkstra()
    {
    	priority_queue<pii,vector<pii>,greater<pii>> pq;
    	memset(dist,0x3f,sizeof dist);
    	memset(mn,0x3f,sizeof mn);
    	pq.push({0,1});dist[1] = 0;
    	while(!pq.empty())
    	{
    		auto _ = pq.top();pq.pop();
    		int u = _.y,d = _.x;
    		if(st[u])	continue;
    		st[u] = 1;
    		for(int i = ver[u];~i;i = succ[i])
    		{
    			int v = edge[i],c1 = cost[i];
    			if(mn[v] > c1)
    			{
    				for(int j = ver[v];~j;j = succ[j])
    				{
    					int z = edge[j],c2 = cost[j];
    					if(dist[z] > d + (c1 + c2) * (c1 + c2))
    					{
    						dist[z] = d + (c1 + c2) * (c1 + c2);
    						pq.push({dist[z],z});
    					}
    				}
    				mn[v] = c1;
    			}
    		}
    	}
    }
    
    int main() 
    {
    	memset(ver,-1,sizeof ver);
    	int n,m;scanf("%d%d",&n,&m);
    	forn(i,1,m)
    	{
    		int u,v,w;scanf("%d%d%d",&u,&v,&w);
    		add(u,v,w);add(v,u,w);
    	}
    	
    	dijkstra();
    	
    	forn(i,1,n)	if(dist[i] == 0x3f3f3f3f)	printf("-1 ");else printf("%d ",dist[i]);
    	return 0;
    }
    
  • 相关阅读:
    Jq操作表格
    最新web 2.0 配色 (一)
    Jqtable edit
    Jq公告渐隐弹出
    Jq模拟最大化最小化关闭
    JqtoggleClass
    Jq弹出公告
    jquery左右拉效果
    最新web 2.0 配色 (二)
    ZenCoding各个工具安装
  • 原文地址:https://www.cnblogs.com/HotPants/p/14415009.html
Copyright © 2011-2022 走看看