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即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章