2020ICPC上海 D - Walker M - Gitignore

D:

首先显然要二分,判断当前二分的mid时间下是否能满足走满0~n

枚举所有情况,这里按照左,右起点p1,p2分别讨论

p1向左 p2向左(以下向左和向右都代表向左或者向右到墙,而不代表初速度方向),只需要计算p1或者p2反弹之后还能走距离n就是合法

p1向左 p2向右,这里有四种情况

四种只需要判断上半部分相加是否大于等于n即可

p1向右 p2向左

只需要判断上下一个分支拐弯之后只要有一个可以多走>=n 或者上下两个分支都能走到墙,也就是拐弯之后可以多走的距离>=0即可

p1向右 p2向右:同第一种情况

代码实现

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define u1 (u<<1)
#define u2 (u<<1|1)
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define sd(x) scanf("%Lf",&x)
#define sdd(x,y) scanf("%Lf %Lf",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _pre(i,a,b) for(int i=(a);i>=(b);--i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=1e6+10,INF=4e18;
double n,p1,p2,v1,v2;
double eps=1e-10;
bool deal(double a)
{
	double t1l=p1/v1;
	double t1r=(n-p1)/v1;
	double t2l=p2/v2;
	double t2r=(n-p2)/v2;
	//左左		
	double s1=-1,s2=-1;
	if(t1l<=a)
	{
		double t1=a-t1l;
		s1=t1*v1;
	}
	if(t2l<=a)
	{
		double t2=a-t2l;
		s2=t2*v2;
	}
	if(s1>=n||s2>=n)return true;
	
	//左右
	s1=-1,s2=-1;
	if(t1l<=a)
	{
		double t1=a-t1l;
		s1=max(t1*v1,t1*v1/2+p1);
	}
	if(t2r<=a)
	{
		double t2=a-t2r;
		s2=max(t2*v2,t2*v2/2+(n-p2));
	}
	if(s1+s2>=n)return true;
	
	//右左
	s1=-1,s2=-1;
	if(t1r<=a)
	{
		double t1=a-t1r;
		s1=t1*v1;
	}
	if(t2l<=a)
	{
		double t2=a-t2l;
		s2=t2*v2;
	}
	if(s1>=n||s2>=n||(s1>0&&s2>0))return true;
	
	
	//右右
	s1=-1,s2=-1;
	if(t1r<=a)
	{
		double t1=a-t1r;
		s1=t1*v1;
	}
	if(t2r<=a)
	{
		double t2=a-t2r;
		s2=t2*v2;
	}
	if(s1>=n||s2>=n)return true;
	return false;
}
bool check(double a)
{
	return deal(a);
}
void solve()
{
//    cin>>n;
	scanf("%Lf",&n);
	scanf("%Lf",&p1);
	scanf("%Lf",&v1);
	scanf("%Lf",&p2);
	scanf("%Lf",&v2);
	double l=0,r=1e9;
	if(p1>p2)
	{
		swap(p1,p2);
		swap(v1,v2);
	}
	while(r-l>eps)
	{
		double mid=(r+l)/2;
		check(mid)?r=mid:l=mid;
	}
	printf("%.10Lf\n",l);
	return ;
}
signed main()
{
	int T=1;
	sf(T);
	while(T--)
		solve();
	return 0;
}

M:

一个类似树形的思想,把所有的可删/不可删的文件映射为下标,然后按照文件路径建树,注意,不同文件夹下的子文件夹名字可能相同,例如

a/b/c和b/b/d并不冲突

所以不是a->b->c这么建树而是a->a/b->a/b/c这样建树

把所有的可以删除的文件,在建树之后就是对应的叶子结点赋权值为0,不可删除的文件对应的叶子节点赋权值为1(这里注意要建立一个虚拟源点并且权值为1,这样假如所有的文件都可以删除的话递归到虚拟源点的时候就会删除,后面会提到)

样例如下

假设某个点的子树(包括自己)权值为0,则向上递归

否则再次找到这个点u的所有儿子,如果儿子的权值为0则res++

此时就能体会出虚拟源点权值为1的好处,假设输入样例为

3 0

a

b

c

那么递归到虚拟源点的时候碰到子树a,b,c就res++三次,答案就为3

代码实现

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define u1 (u<<1)
#define u2 (u<<1|1)
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define sd(x) scanf("%Lf",&x)
#define sdd(x,y) scanf("%Lf %Lf",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _pre(i,a,b) for(int i=(a);i>=(b);--i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=1e6+10,INF=4e18;
int n,m;
map<string,int>mp;
int idxx;
int e[N],ne[N],h[N],w[N],idx;
int sz[N];
map<PII,bool>pan;
void add(int a,int b)
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
string s;
void deal(string s,int c)
{
	string now="";
	int pre=1;
	_rep(i,0,(int)s.size()-1)
	{
		if(s[i]=='/')
		{
			if(!mp.count(now))mp[now]=++idxx;
			int t=mp[now];
			if(!pan.count({pre,t}))add(pre,t);
			pan[{pre,t}]=true;
			pre=t;
//			now="";
		}
		now+=s[i];
	}
	if(!mp.count(now))mp[now]=++idxx;
	int t=mp[now];
	w[t]=c;
	add(pre,t);
	pre=t;
//	now="";
	return;
}
int res;
int dfs(int u,int fa)
{
	sz[u]=w[u];
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		sz[u]+=dfs(j,u);
	}
	if(sz[u])
	{
		for(int i=h[u];~i;i=ne[i])
		{
			int j=e[i];
			if(j==fa)continue;
			if(!sz[j])res++;
//			cout<<"j="<<j<<endl;
		}
	}
	return sz[u];
}
void solve()
{
	res=0;
	idxx=1;
	idx=0;
	cin>>n>>m;
	_rep(i,1,n)
	{
		cin>>s;
		deal(s,0);
	}
	_rep(i,1,m)
	{
		cin>>s;
		deal(s,1);
	}
	w[1]=1;
	dfs(1,0);
	_rep(i,0,idxx)h[i]=-1,w[i]=0;
	mp.clear();
	pan.clear();
	cout<<res<<'\n';
	return ;
}
signed main()
{
	IOS;
	memset(h,-1,sizeof(h));
	int T=1;
	cin>>T;
	while(T--)
		solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值