ACM-ICPC 2018 青岛赛区网络预赛(solve6/11)

题目提交地址:http://acm.zju.edu.cn/onlinejudge/showProblems.do?contestId=1&pageNumber=31

A题

题意:给你n和m,问你怎么安排这m个东东可以分数最高和最低。

思想:肯定m个连续起来分数最高,最低的话考虑插空,存在(n-m+1),n是(n-m+1)多少倍就是最少得分多少。

B题

题意:给你一棵树,其中有m个点是红色,并且保证1是根节点而且1是红色的,然后有q次询问,每次有k个点,问你将树上某点变成红色,让当前集合的最大的花费尽量小。问你q次询问花费里面的最小的是多少,输出。对于花费就相等于当前点到离他最近的红色点的路径权值和。

思想:预处理ST表,将k个点的花费排序,从大到小,对于集合的点,肯定是消除最大的那个话费让它变小,这样才能变小。所以枚举其他点,考虑他们的LCA,不断的比较染色(前几个点的LCA和当前点的)的LCA造成的差值。不断的求LCA,对于前边的点肯定是高度不断往上,肯定值会变大,不会比原来小,所以只需要考虑最大的花费的点改变之后的花费和当前点染色变成LCA的花费,取一个最大的即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
struct node{
	int v;
	ll value;
	int next;
}no[maxn<<1];
int n,m,cnt;
int head[maxn];
int flag[maxn];//dfs标记是否访问过 
int vis[maxn];//标记红色点 
int color[maxn];//标记颜色 
int deep[maxn];//深度 
int pre[maxn][30];//ST表
int p[maxn];//存放k个点 
ll dist[maxn];
void init(int _n)
{
	dist[1]=0;
	cnt=0;
	memset(head,-1,sizeof(head[0])*(_n+1));
	memset(vis,0,sizeof(vis[0])*(_n+1));
	memset(flag,0,sizeof(flag[0])*(_n+1));
}
void add(int u,int v,ll value)
{
	no[cnt].v=v;
	no[cnt].value=value;
	no[cnt].next=head[u];
	head[u]=cnt++;
	
	no[cnt].v=u;
	no[cnt].value=value;
	no[cnt].next=head[v];
	head[v]=cnt++;
}
void dfs(int u,int co,int fa,int de)
{
	flag[u]=1;
	color[u]=co;
	pre[u][0]=fa;
	deep[u]=de;
	for(int i=head[u];i!=-1;i=no[i].next)
	{
		int v=no[i].v;
		if(flag[v])
			continue;
		dist[v]=dist[u]+no[i].value;
		if(vis[v])
			dfs(v,v,u,de+1);
		else	
			dfs(v,co,u,de+1);
	}
}
void init_lca()
{
	for(int j=1;(1<<j)<=n;j++)
		for(int i=1;i<=n;i++)
			pre[i][j]=pre[pre[i][j-1]][j-1];
}
int lca(int x, int y)
{
    if(deep[x] < deep[y]) 
		swap(x, y);
    int temp = 0;
    while((1<<temp) <= deep[x])
		temp++;
    temp--;
    for(int i = temp;i >= 0;i--)
    {
        if(deep[x] - (1<<i) >= deep[y])
            x = pre[x][i];
    }
    if(x == y) 
		return x;
    for(int i = temp;i >= 0;i--)
    {
        if( pre[x][i] != pre[y][i])
        {
            x = pre[x][i];
			y = pre[y][i];
		} 
    }
    return pre[x][0];
}
int cmp(int a,int b)//花费从大到小 
{
	return (dist[a]-dist[color[a]]) > (dist[b]-dist[color[b]]); 
}
int main()
{
	int t,q;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&m,&q);	
		init(n); 
		for(int i=0;i<m;i++)
		{
			int temp;
			scanf("%d",&temp);
			vis[temp]=1;
		}
		for(int i=1;i<n;i++)
		{
			int a,b;
			ll value;
			scanf("%d%d%lld",&a,&b,&value);
			add(a,b,value);
		}
		dfs(1,1,0,1);//u color father deep 
		init_lca(); 
		while(q--)
		{
			int k;
			scanf("%d",&k);
			for(int i=0;i<k;i++)
				scanf("%d",&p[i]);
			sort(p,p+k,cmp);
			int now = p[0];
			int i=0;
			ll ans = (1ll<<62)-1;
			while(i<k)
			{
				int LCA=lca(now,p[i]);
				while(i<k && LCA == lca(now,p[i]) && color[now]==color[p[i]])//公共祖先是一个 而且 是同一个红色染色 
					i++;
				ll res = dist[p[0]]-dist[color[p[0]]] - dist[LCA] + dist[color[LCA]];
				if(i<k)
					res=max(res,dist[p[i]]-dist[color[p[i]]]);
				if(res<ans)
					ans=res;
				else
					break;
				now = LCA;
			}
			printf("%lld\n",ans);
		} 
	}
	return 0;
} 

 

 

C题

题意:给你n次操作,每次操作都是五种操作中的一种,问你是否是一个死循环程序。

思想:对于一个操作如果进行2次肯定就是死程序了,所以只需要判断操作的编号即可。

H题

题意:给你一个字符串,1表示可以走(花1s),0表示等1s再走(也就是花2s),然后让你求一个

和。

思想:从后往前考虑下,最后一个点单独考虑,如果最后一个点是0那就是花2s,如果是1花1s。

然后对于其他的从后往前考虑下:如果当前是0的话,那么对于后边的是没有影响的,因为你等2s之后后边的还是原来的样子,所以只需要结果加上2*(len-i)  代表的是后边需要加多少个t(p,q),每一个都需要增加2s。对于当前是1的话,考虑下1s之后的问题。如果往后一个是1的话,那么你直接过去之后你还需要2s才能过去,然而你当时计算的时候只是1s,所以这样的话结果就需要增加(len-i) (对于当前1来说都需要加1s) + (len-i-1)(对于后边的从1变成0又多了1s)。如果往后一个是0的话,那么你等1s过去0就变成了1s了,这样时间就少了,所以结果需要(len-i)-(len-i-1) (与上同理)

#include<bits/stdc++.h>
using namespace std;
char str[100005];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s",str);	
		int len=strlen(str);
		long long ans,pre;
		if(str[len-1]=='1')//最后一个单独考虑下 
		{
			pre=1;
			ans=1;
		}
		else
		{
			pre=2;
			ans=2;
		}
		for(int i=len-2;i>=0;i--)
		{
			if(str[i]=='1')
			{
				if(str[i+1]=='0')
					pre+=(len-i)-(len-i-1);
				else
					pre+=(len-i)+(len-i-1);
				ans+=pre;
			}
			else//当前是0  所有的的结果都需要加上2 
			{
				pre+=(len-i)*2;
				ans+=pre; 
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

J题

题意:a,b,c,d,t,v; 代表的是a的倍数按灯b下,c的倍数按灯d下,每次按完之后灯会亮v秒,总共是ts

思想:求最后那个valu,考虑下对于a和c来说肯定有一个值是交接点,题意就可以分解成,有多少个是a,c独立的区间(循环节)*这个区间的valu+非独立区间的结果即可。所以就可以变成模拟2个区间的情况求解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		ll a,b,c,d,v,t;
		scanf("%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&v,&t);
		ll ans1 = -1;
		ll ans2 = 0;
		ll lca = a/__gcd(a,c)*c;
		ll m = t%lca;
		ll C= t / lca;
		ll tdata = 0;
		ll i = 1ll;
		ll j = 1ll;
		if(C==0)
			lca = m;
		int flag=1;
		while(1)
		{
			ll t;
			if(a*i<c*j)
			{
				t=a*i;
				if(t>m)
					break;
				i++;
			}
			else
			{
				t=c*j;
				if(t>m)
					break;
				j++;
			}
			if(t-tdata>v)
				ans1--;
			tdata=t;
		}
		ans1 = ans1 +i*b+j*d;
		if(C)
		{
			tdata=0;
			i=j=1;
			while(1)
			{
				ll t;
				if(a*i<c*j)
				{
					t=a*i;
					if(t>lca)
						break;
					i++;
				}
				else
				{
					t=c*j;
					if(t>lca)
						break;
					j++;
				}
				if(t-tdata>v)
					ans2--;
				tdata=t;
			}
			ans2=ans2+(i-1)*b+(j-1)*d;
			ans2*=C;
		}
		printf("%lld\n",ans1+ans2);
	}
	return 0; 
} 

K题

题意:就是给你n个数,然后求一个集合保证集合中任何2个数XOR的值小于这两个数的最小值。

思想:转换成求一个最高位是1而且高度一样的数最多的那个即可。对于每个数只要最高位是第几位,然后每次对于判断的位置取max即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值