zoukankan      html  css  js  c++  java
  • ICPC World Finals 2019部分题目解析

    #6577. 「ICPC World Finals 2019」瓷砖

    description

    有两排瓷砖,分为前排和后排,每一排都有(N)个。且对于每块瓷砖都已知其高度(h_i)和价值(w_i)
    现在你可以对同一行的瓷砖进行排序,要求对于同一排瓷砖价值单调不减,对于同一列后排瓷砖高度严格大于前排瓷砖
    如果无解输出(impossible)

    data range

    (Nle 5cdot10^5)

    solution

    对于每一行,容易想到先按照价格从小到大排序
    现在的关键就在于对于价格相同的一段如何进行排列
    不妨设当前后排有(a)个最便宜的,前排有(b)个最便宜的
    不妨假设(ble a)
    那么我们对前排的(b)个一个一个地考虑
    如果当前瓷砖的高度为(h_i),那么贪心地想,我们只用在后排的(a)个中选出高度最小的瓷砖满足它的高度大于(h_i)
    对于(b>a),讨论是类似地
    我们可以用(set)维护以上的所有操作

    time complexity

    容易发现我们使用(mathcal O(log_2n))的时间使问题规模减小(1)
    因此总复杂度(mathcal O(nlog_2n))

    code

    #include<bits/stdc++.h>
    #define mp make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef pair<int,int> pii;
    const int N=5e5+5;
    int n;
    int pp[N],ph[N],sp[N],sh[N],a1[N],a2[N];
    set<pii>s1,s2;
    pii p1[N],p2[N];
    inline bool work()
    {
    	for(int i=1;i<=n;++i)
    		p1[i]=mp(sp[i],i),p2[i]=mp(pp[i],i);
    	sort(p1+1,p1+n+1),sort(p2+1,p2+n+1);
    	int cnt=0;bool pd1=0,pd2=0;
    	while(cnt<n)
    	{
    		int d=cnt+1;
    		if(!pd1)for(int i=d;i<=n&&p1[i].fi==p1[d].fi;++i)
    			s1.insert(mp(sh[p1[i].se],p1[i].se));
    		if(!pd2)for(int i=d;i<=n&&p2[i].fi==p2[d].fi;++i)
    			s2.insert(mp(ph[p2[i].se],p2[i].se));
    		if(s1.size()>=s2.size())
    		{
    			for(auto v:s2)
    			{
    				++cnt;auto it=s1.upper_bound(mp(v.fi,n));
    				if(it==s1.end())return 0;
    				a1[cnt]=(*it).se,a2[cnt]=v.se;s1.erase(it);
    			}
    			pd1=1,pd2=0;s2.clear();
    		}
    		else
    		{
    			for(auto v:s1)
    			{
    				++cnt;auto it=s2.lower_bound(mp(v.fi,1));
    				if(it==s2.begin())return 0;it--;
    				a1[cnt]=v.se,a2[cnt]=(*it).se;s2.erase(it);
    			}
    			pd1=0,pd2=1;s1.clear();
    		}
    	}return 1;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)scanf("%d",&sp[i]);
    	for(int i=1;i<=n;++i)scanf("%d",&sh[i]);
    	for(int i=1;i<=n;++i)scanf("%d",&pp[i]);
    	for(int i=1;i<=n;++i)scanf("%d",&ph[i]);
    	if(!work())puts("impossible");
    	else
    	{
    		for(int i=1;i<=n;++i)printf("%d ",a1[i]);puts("");
    		for(int i=1;i<=n;++i)printf("%d ",a2[i]);
    	}
    	return 0;
    }
    

    #6581. 「ICPC World Finals 2019」断头路探测者

    description

    给定(N)个点(M)条边的无向图。如果从(x)进入某条边(S)后只能掉头返回(x),那么就应该(S)在入口(x)处添加一个标志
    如果在某条边(S)的入口(x)处有一个标志,另一条边的入口(y)处有一个标志,且存在一条从(x)出发经过(S)到达(y)的路径
    那么我们就称在(y)处的标志是不必要的
    现在询问标志的最小数量,同时要求输出方案

    data range

    (N,Mle 5cdot 10^5)

    solution

    此题很妙妙
    对于每一个联通块单独考虑
    如果这个联通块是一棵树
    考虑树上的一条边(u ightarrow v),其中(u)不是叶子节点
    那么必然存在一条从某个叶子节点出发到(u)的路径,因此这个标志是不必要的
    因此对于一棵树,它的标志就应该在边(u ightarrow v)上,其中(u)是一个叶子节点
    如果这个联通块存在环,那么环上的边和由其他地方指向环的边都是不应该有标志的
    于是乎我们只需要给那些由环到树的边上添加标志即可
    具体实现可以采用拓扑排序
    第一次剥离环上所有的树
    第二次统计和环联通的点
    然后根据所得信息就可以较容易地统计以上两种情况了

    time complexity

    (mathcal O(n+m))忽略了排序的复杂度

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+5;
    int n,m,d[N];
    bitset<N>vis;
    vector<int>e[N];
    queue<int>q;
    vector<pair<int,int> >ans;
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1,u,v;i<=m;++i)
    	{
    		scanf("%d%d",&u,&v);
    		e[u].push_back(v),e[v].push_back(u);
    		++d[u],++d[v];
    	}
    	for(int i=1;i<=n;++i)
    		if(d[i]==1)vis[i]=1,q.push(i);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int v:e[u])
    			if(!vis[v]&&--d[v]==1)vis[v]=1,q.push(v);
    	}
    	vis.reset();
    	for(int i=1;i<=n;++i)
    		if(d[i]>1)vis[i]=1,q.push(i);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int v:e[u])
    			if(!vis[v])vis[v]=1,q.push(v);
    	}
    	for(int u=1;u<=n;++u)
    		for(int v:e[u])
    		{
    			if(!vis[u]&&e[u].size()==1)ans.push_back(make_pair(u,v));
    			if(vis[u]&&d[u]>1&&d[v]==1)ans.push_back(make_pair(u,v));
    		}
    	sort(ans.begin(),ans.end());
    	printf("%lu
    ",ans.size());
    	for(auto i:ans)printf("%d %d
    ",i.first,i.second);
    	return 0;
    }
    

    #6584. 「ICPC World Finals 2019」Hobson 的火车

    description

    给出一个(N)阶有向图,满足每个点的出度为(1)
    对于每个点求到它的距离不大于(k)的点的个数

    data range

    (kle Nle 5cdot 10^5)

    solution

    还是对于每个联通块单独考虑
    容易发现是一个内向基环树
    可以理解为有一个简单环,在其上套了若干个树,满足根节点在环上且边都由儿子指向父亲
    显然,树上的点和环上的点计算答案的方式是不同的
    对于树上的点,到它距离不超过(k)的点一定在它的子树中
    直接统计不方便,考虑拆开贡献
    一个点的答案就是以自己为根的子树大小减去所有(k+1)级后代的子树大小(如果存在的话)
    对于每个点,设子树大小为(sz)
    那么对自己的贡献就是(sz),对自己(k+1)级祖先的贡献就是(-sz)
    可以通过(vector)来维护当前节点的所有祖先,然后就能做到线性了
    对于环上的点,考虑每个点对于环上的点的贡献
    分为两个部分
    首先环上连续的一段都会被贡献整个树的点数(这部分也有可能不存在)
    然后之后一部分,每再走一步,贡献就会相应的减少
    第一部分可以差分,第二部分暴力跳就可以了
    注意还有一些细节需要处理

    time complexity

    (mathcal O(n))

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+5;
    int n,k,to[N],ans[N],dep[N],sz[N],num[N],a[N],dif[N],id[N];
    vector<int>pre[N],now;
    bool vis[N];
    void dfs(int u,int pr,vector<int>&p)
    {
    	sz[u]=1;now.push_back(u);vis[u]=1;
    	p.push_back(u);
    	for(auto v:pre[u])
    	{
    		dep[v]=dep[u]+1;
    		p.size()>k?dfs(v,pr+1,p):dfs(v,pr,p);
    		p.pop_back();sz[u]+=sz[v];
    	}
    	ans[u]+=sz[u];
    	if(pr>=0)ans[p[pr]]-=sz[u];
    }
    inline void ad(int l,int r,int v){dif[l]+=v,dif[r+1]-=v;}
    inline void add(int l,int r,int len,int v)
    {
    	l<=r?ad(l,r,v):(ad(l,len,v),ad(1,r,v));
    }
    inline void work(int u)
    {
    	int v=to[u],cnt=0;
    	while(u!=v)u=to[u],v=to[to[v]];
    	for(;!vis[u];u=to[u])vis[u]=1,a[++cnt]=u,dif[cnt]=0,id[u]=cnt;
    	for(int p=to[u];;p=to[p])
    	{
    		++ans[p];now=vector<int>(1,p);
    		for(auto v:pre[p])
    			if(!vis[v]){vector<int>a(1,p);dep[v]=1;dfs(v,-1,a);ans[p]+=sz[v];}
    		int mx=0;
    		for(auto v:now)num[dep[v]]=0,mx=max(mx,dep[v]);
    		for(auto v:now)++num[dep[v]];
    		for(int i=1;i<=mx;++i)num[i]+=num[i-1];
    		int sl=min(k-mx,cnt-1),pt=a[(id[p]+sl)%cnt+1],t;
    		if(sl>0)add(id[p]%cnt+1,(id[p]+sl-1)%cnt+1,cnt,num[mx]),t=mx-1;
    		else t=min(k-1,mx),pt=to[p];
    		while(~t&&pt!=p)ans[pt]+=num[t],--t,pt=to[pt];
    		if(p==u)break;
    	}
    	for(int i=1;i<=cnt;++i)dif[i]+=dif[i-1];
    	for(int i=1;i<=cnt;++i)ans[a[i]]+=dif[i];
    }
    int main()
    {
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%d",&to[i]);
    		pre[to[i]].push_back(i);
    	}
    	for(int i=1;i<=n;++i)if(!vis[i])work(i);
    	for(int i=1;i<=n;++i)printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    UWP Xaml设计器中输入特殊字符
    UWP DEP0700: 应用程序注册失败。[0x80073CF9] Install failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CF9)
    UWP 使用Windows.Media.FaceAnalysis.FaceDetector检测人脸
    .Net Core和.Net Standard直观理解
    双屏互动h5
    前端发展趋势与开发技巧
    git学习总结
    前端路由
    【react开发】使用swiper插件,loop:true时产生的问题解决方案
    【纪念】我的大学同学名单
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14195028.html
Copyright © 2011-2022 走看看