2018-11-1杂题选讲

2018-11-1杂题选讲

T1 黄金阵容均衡

原题
总共有 种能力编号为 1到m 。一共 n天,每天都会有一些能力得到一次提升,每天的能力提升都用一个数字表示,
比如数字 13,转化为二进制为1101 ,并且从右往左看,表示编号为1,3,4 的能力分别得到了一次提升,如果在连续的一段时间内,每项能力都提升了相同的次数,就称这段时间为一个均衡时期。给出n 天的数字,求出均衡时期的最大长度。(N<=100000,M<=30)

若记录每个能力被增强的前缀和,那么某两天的所有前缀和相减出来的值都相等,说明这段时间所有技能增量相同,则是一个均衡时段。若需要某两天的所有前缀和相减出来的值都相等,那么这两天的能力值的“凹凸形状”必需一样,这样才能保证相减之后是平的。记录每种“凹凸形状”第一次出现的位置,若某天的“凹凸形状”已经出现过,则这段均衡时间的长度为该天与该形状第一次出现的天相减。
例如:“23232” 和 “01010” 的 “凹凸形状” 一样因为他们相减得 “22222” ,所以认为 “23232” 也是 “01010”

提交记录

#include<bits/stdc++.h>
using namespace std;
int n,m,ans=0;
map <vector<int>,int> tms;
int main()
{
    scanf("%d%d",&n,&m);
    vector <int> present(m);
    tms[present]=0;
    for (int i=1;i<=n;i++)
    {
        int tmp,minus=1;
        scanf("%d",&tmp);
        for (int j=1;j<=m;j++) if (tmp&(1<<(j-1))) present[j-1]++;
        for (int j=1;j<=m;j++) if (present[j-1]==0) minus=0;
        if (minus) for (int j=1;j<=m;j++) present[j-1]--;
        if (tms.count(present)) ans=max(ans,i-tms[present]);
        else tms[present]=i;
    }
    printf("%d",ans);
    return 0;
}

T2 Senior Pan

原题
给你一个n点m边的带边权有向图,其中有k个点为特殊点 询问 k个点之间最短的一条道路

将特殊点分为两组,每组里面自己的边不走,A组连向源点,B组连向汇点,跑一边S到T的最短路就会算出一种有可能的答案。改变分组方案,保证每对点都在不同对组同时出现过,算出答案。

#include<bits/stdc++.h>
#define N 500010
using namespace std;
long long T;
long long n,m,k,vital[N<<4],first[N<<4],nxt[N<<4],u[N<<4],v[N<<4],w[N<<4],group[N<<4],tot=0,s,t,ans;
long long vis[N<<4],dis[N<<4],myans[N<<4],cnt=0;
int pp;
typedef long long ll;
struct point
{
	ll num,dist;
	bool operator>(const point &a) const{return dist<a.dist;}
}p[N<<4];
priority_queue <point,vector<point>,greater<point> > q;
void add(ll from,ll to,ll cost)
{
	tot++;
	nxt[tot]=first[from];
	first[from]=tot;
	u[tot]=from;
	v[tot]=to;
	w[tot]=cost;
	return;
}
ll Dijkstra()
{
	for (int i=1;i<=t;i++) p[i].num=i,p[i].dist=1e18;
	while (!q.empty()) q.pop();
	for (int i=1;i<=n;i++) dis[i]=1e18;
	dis[t]=1e18;
	dis[s]=0;
	p[s].dist=0;
	q.push(p[s]);
	while (!q.empty())
	{
		point tmp=q.top();
		q.pop();
		for (int j=first[tmp.num];j!=-1;j=nxt[j]) if ((!(group[u[j]]==1&&group[v[j]]==1))&&(!(group[u[j]]==2&&group[v[j]]==2))) if (dis[v[j]]>dis[u[j]]+w[j]) 
		{
			dis[v[j]]=dis[u[j]]+w[j];
			p[v[j]].dist=dis[u[j]]+w[j];
			q.push(p[v[j]]);
		}	
	}
	return dis[t];
}
int main()
{
	scanf("%lld",&T);
	while (T--)
	{
		memset(first,-1,sizeof(first));
		memset(vital,0,sizeof(vital));
		ans=1e18;
    	ll x,y,z;
		scanf("%lld%lld",&n,&m);
		s=n+1;
		t=n+2;
		for (int i=1;i<=m;i++) scanf("%lld%lld%lld",&x,&y,&z),add(x,y,z),add(y,x,z);
		scanf("%lld",&k);
		for (int i=1;i<=k;i++) scanf("%d",&pp),vital[pp]=1; 
		for (int i=0;i<=log2(n);i++) 
		{
			memset(group,0,sizeof(group));
			for (int j=1;j<=n;j++) if (vital[j]) 
			{
				if ((1<<i)&j) add(s,j,0),group[j]=1;
		    	else add(j,t,0),group[j]=2;
			}
			ans=min(ans,Dijkstra());
			s+=2;
			t+=2;
		}
		myans[++cnt]=ans;
	}
	for (int i=1;i<=cnt;i++) printf("%lld\n",myans[i]);
    return 0;
}

T5 Go Home

原题
来自 N个房子的人操作着同一辆车从 S点出发,每个人都绝顶聪明且想尽早回到家(都和 S在 x轴上)。
每个还在车上的人都会选择往左开或者往右开。

分两种情况:
1,如果所有人都在车的同一边,则直接往一边开到底
2,如果车的两边都有人,那么取最靠边的两个房子,其中人更少的房子肯定是终点站,那么既然这个房子的人知道自己不可能比另一个房子的人先回家,也就是说如果另一个房子的人回不了家,他们也不能,于是他们就会帮另一个房子的人投票,于是票数叠加,答案更新,点数减一,慢慢删点会变成情况1。
*注意:不是每一次删点都需要更新答案,因为从人多的房子到那个人少的房子路上可能经过一些房子,用一个while来删点就行了。

提交记录

#include<bits/stdc++.h>
using namespace std;
long long n,m,s,ans=0,l,r,x[100010],p[100010];
int main()
{
	scanf("%lld%lld",&n,&s);
	for (int i=1;i<=n;i++) scanf("%lld%lld",&x[i],&p[i]);
	l=1;
	r=n;
	while (x[l]<=s&&s<=x[r])
	{
		ans+=x[r]-x[l];
		if (p[l]<p[r]) while (p[l]<p[r]&&x[l]<=s&&s<=x[r]) p[r]+=p[l],l++;
		else while (p[l]>=p[r]&&x[l]<=s&&s<=x[r]) p[l]+=p[r],r--;
	}
	if (x[l]>=s) ans+=x[r]-s;
	else if (x[r]<=s) ans+=s-x[l];
	printf("%lld",ans); 
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值