CODE FESTIVAL 2015 決勝(部分)

CODE FESTIVAL 2015 決勝(部分)

B - ダイスゲーム

题意:

(N)(6)个面骰子,然后问掷到概率最大的点数是多少。

分析:

随便打表随便发现是(hugeleftlfloor N*7/2 ight floor)

代码略。

C - 寿司タワー

题意:

给出(N)和一个长度为(2N)(01)串,可以用(1)的花费移动其中的任意一个数字,询问使从头开始使每两个都含有(0)(1)的最小交换次数。

例如:(1001)是一个合法的串,每两个指((1,2),(3,4)....(2N-1,2N))都满足要求。

分析:

发现最多需要(n)次才可以交换得到正确的序列。

如果当下已经有一个(0)(1)相邻,那么就可以减少一次交换次数。模拟即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+7;
int n,ans;
char ch[MAXN];
int main()
{
	cin>>n;
	cin>>ch
	
	
	
	;
	ans=2*n;
	for(int i=0;i<=2*n-1;i++)
	{
		if(ch[i]=='0'&&ch[i+1]=='1'||(ch[i]=='1'&&ch[i+1]=='0')){
			ans-=2;
			i++;
		}
	}
	cout<<ans/2<<endl;
}

H - 焼肉の達人

题意:

给出(N)个区间以及大区间长度(M),选择部分区间对其进行区间覆盖,其中覆盖了两次及以上的部分不计算贡献,求最多覆盖大区间中的多少。

分析:

做法非常神仙,貌似也有线段树的做法?没有仔细看。

思考贡献的产生以及消失。当选择了一个区间时,这个区间内则都算贡献,当区间重合时,重合部分不算贡献。

观察可以得到如果我们将每一个整点都向前向后连边长度为(1),表示这一部分并没有计算贡献,然后对于一段区间,我们将其从头连到尾长度为0表示计算贡献,可以发现跑从(0)(n)的最短路就是我们没有计算贡献的最小值,可以堆优化Dijkstra跑一跑。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+7;
struct po
{
	int nxt,to,dis;
}edge[MAXN<<3];
struct Node
{
	int dis,id;
	Node(){}
	Node(int dis,int id) : dis(dis), id(id){}
	bool operator < (const Node& rhs) const
	{
		return dis>rhs.dis;
	}
};
int dis[MAXN],b[MAXN],head[MAXN],num;
inline void add_edge(int from,int to,int dis)
{
	edge[++num].nxt=head[from];
	edge[num].to=to;
	edge[num].dis=dis;
	head[from]=num;
}
void Dijkstra(int s)
{
	memset(dis,100,sizeof(dis));
	priority_queue<Node> q;
	q.push(Node(0,s));
	while(!q.empty()){
		Node u=q.top();q.pop();
		if(b[u.id]) continue;
		b[u.id]=1;
		for(int i=head[u.id];i!=-1;i=edge[i].nxt){
			int v=edge[i].to;
			if(dis[v]>u.dis+edge[i].dis){
				dis[v]=u.dis+edge[i].dis;
				q.push(Node(dis[v],v));
			}
		}
	}
}
int n,m,s,t;
int main()
{
	cin>>n>>m;
	memset(head,-1,sizeof(head));
	for(int i=0;i<m;i++){
		add_edge(i,i+1,1);
		add_edge(i+1,i,1);
	}
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		add_edge(x,x+y,0);
	}
	Dijkstra(0);
	cout<<m-dis[m]<<endl;
}

I - 風船ツリー

题意:

给出一个有(N)个节点的气球树,以及每个气球到它父亲的边的长度。

现在给出(Q)组询问,每组询问询问使气球树的最大高度为(H)最少需要删掉多少条边。

分析:

思维巧妙的线段树题目,也不知道(ATCODER)啥时候能出出来这么NB的线段树题了。

思考删边的时候,每个子树会互相影响,所以并不能简单地一删了之。

观察发现询问明显不可以使用数据结构什么的东西维护,这东西并不具有结合性质。所以估计是预处理之后进行$O(1)回答。

当只有一组询问时,考虑做法,可以先找到这个深度的所有点,然后枚举那些是要留下来的,删去其连向儿子的边以及所有其他子树有深度大于(x)的边。并且可以发现并没有比这更优的做法。

那么是否可以对所有深度进行预处理,然后直接回答询问?

发现不同的深度最多只有(N)个,离散化一下,然后我们就能使用线段树对其进行维护了。

可以先(dfs)一下求出每个点向下连出的边的条数以及以它为根的子树到达的最大深度是多少,然后从(1)号点开始,对于我们进入的节点,删去其所有的儿子,具体是将其子树的深度一直到(n)都在线段树上打上标记,然后当遍历一个新的节点时,将这个节点添加回来,重复刚才的操作,这样就可以(O(nlogn))求出对于任何一个深度,我们需要删掉多少条边,然后每次询问就可以(O(logn))查询了。

#include <bits/stdc++.h>
#define mid (l+r>>1)
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int MAXN=1e5+7;
int add[MAXN<<2],n,m,sz,ll[MAXN],q;
inline void pushup(int rt)
{
	add[rt]=add[ls]+add[rs];
}
inline void update(int l,int r,int rt,int x,int k)
{
	if(l==r) add[rt]+=k;
	else {
		if(x<=mid) update(l,mid,ls,x,k);
		else update(mid+1,r,rs,x,k);
		pushup(rt);
	}
}
inline int query(int L,int R,int l,int r,int rt)
{
	int ans=0;
	if(L<=l&&r<=R) return add[rt];
	if(L<=mid) ans+=query(L,R,l,mid,ls);
	if(R>mid) ans+=query(L,R,mid+1,r,rs);
	return ans;
}
struct po
{
	int nxt,to;
}edge[MAXN];
int head[MAXN],mx[MAXN],dep[MAXN],cnt[MAXN],dp[MAXN],num;
vector<int> vx;
inline void add_edge(int from,int to)
{
	edge[++num].nxt=head[from];
	edge[num].to=to;
	head[from]=num;
}
void dfs(int u,int fa)
{
	mx[u]=dep[u];
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v!=fa){
			dfs(v,u);
			mx[u]=max(mx[u],mx[v]);
		}
	}
}
inline void solve(int u,int fa)
{
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v!=fa) update(0,n+2,1,mx[v],1);
	}
	dp[dep[u]]=min(dp[dep[u]],query(dep[u]+1,vx.size()+1,0,n+2,1));
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==fa) continue;
		update(0,n+2,1,mx[v],-1);
		solve(v,u);
		update(0,n+2,1,mx[v],1);
	}
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v!=fa) update(0,n+2,1,mx[v],-1);
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>ll[i];
		dep[i]+=ll[i];
	}
	for(int i=2;i<=n;i++){
		int p; cin>>p;
		add_edge(p,i);
		dep[i]+=dep[p];
		vx.push_back(dep[i]);
	}
	
	vx.push_back(dep[1]);
	sort(vx.begin(),vx.end());
	vx.erase(unique(vx.begin(),vx.end()),vx.end());
	for(int i=1;i<=n;i++) dep[i]=lower_bound(vx.begin(),vx.end(),dep[i])-vx.begin();
	
	for(int i=0;i<vx.size();i++) dp[i]=n;
	dfs(1,1);
	solve(1,1);
	cin>>q;
	for(int i=1;i<=q;i++){
		int h;
		cin>>h;
		int pos=lower_bound(vx.begin(),vx.end(),h)-vx.begin();
		if(pos==vx.size()||vx[pos]!=h) puts("-1");
		else cout<<dp[pos]<<endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/victorique/p/9843595.html