原题
总共有 种能力编号为 1到m 。一共 n天,每天都会有一些能力得到一次提升,每天的能力提升都用一个数字表示,
比如数字 13,转化为二进制为1101 ,并且从右往左看,表示编号为1,3,4 的能力分别得到了一次提升,如果在连续的一段时间内,每项能力都提升了相同的次数,就称这段时间为一个均衡时期。给出n 天的数字,求出均衡时期的最大长度。(N<=100000,M<=30)
若记录每个能力被增强的前缀和,那么某两天的所有前缀和相减出来的值都相等,说明这段时间所有技能增量相同,则是一个均衡时段。若需要某两天的所有前缀和相减出来的值都相等,那么这两天的能力值的“凹凸形状”必需一样,这样才能保证相减之后是平的。记录每种“凹凸形状”第一次出现的位置,若某天的“凹凸形状”已经出现过,则这段均衡时间的长度为该天与该形状第一次出现的天相减。
例如:“23232” 和 “01010” 的 “凹凸形状” 一样因为他们相减得 “22222” ,所以认为 “23232” 也是 “01010”
提交记录
#include
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;
}
原题
给你一个n点m边的带边权有向图,其中有k个点为特殊点 询问 k个点之间最短的一条道路
将特殊点分为两组,每组里面自己的边不走,A组连向源点,B组连向汇点,跑一边S到T的最短路就会算出一种有可能的答案。改变分组方案,保证每对点都在不同对组同时出现过,算出答案。
#include
#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;
}
原题
来自 N个房子的人操作着同一辆车从 S点出发,每个人都绝顶聪明且想尽早回到家(都和 S在 x轴上)。
每个还在车上的人都会选择往左开或者往右开。
分两种情况:
1,如果所有人都在车的同一边,则直接往一边开到底
2,如果车的两边都有人,那么取最靠边的两个房子,其中人更少的房子肯定是终点站,那么既然这个房子的人知道自己不可能比另一个房子的人先回家,也就是说如果另一个房子的人回不了家,他们也不能,于是他们就会帮另一个房子的人投票,于是票数叠加,答案更新,点数减一,慢慢删点会变成情况1。
*注意:不是每一次删点都需要更新答案,因为从人多的房子到那个人少的房子路上可能经过一些房子,用一个while来删点就行了。
提交记录
#include
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;
}