目录
1003.Slipper
题意:有一棵树,树上路径有边权.我们可以花费p的代价,从x走到x+k或者x-k,给出起点终点求最短距离。
思路:一眼dij+建立虚点.可是这个虚点建立会有一点问题.我一开始是对于x+k和x-k向着x建议一条边.这里就会出现问题,这样的话进行缩点的操作,同层的点也可以互相到达了(用过这一层到x在到这一等的另一个点),此时考虑进行点的拆分,如果我们把上述的每一个虚拟节点拆分成两个节点,一个出节点一个入节点,(x也要进行拆分),这样就不会让同层节点相互链接,因为这样要保证每个点的出节电和入节点相连接才能互相连接,而之后的操作不会吧无关的出入点相连.所以成立.
#include<bits/stdc++.h>
using namespace std;
int h[4000010],vis[4000010],tot=0,d[4000010],n,p,k,s,t;
long long dis[4000010];
vector<int>vv[4000010];
struct node
{
int to,ne,val;
}edge[2*4000010];
struct poi
{
int x;
long long len;
bool operator<(poi c)const{
return c.len<len; //从小到大排序
}
};
void add(int x,int y,int w)
{
edge[++tot].to=y;
edge[tot].val=w;
edge[tot].ne=h[x];
h[x]=tot;
return ;
}
void dfs(int x,int fa)
{
d[x]=d[fa]+1;
vv[d[x]].push_back(x);
for(int i=h[x];i!=-1;i=edge[i].ne)
{
int j=edge[i].to;
if(j==fa)
continue;
dfs(j,x);
}
return ;
}
void dij()
{
memset(vis,0,sizeof vis);
memset(dis,0x3f,sizeof dis);
priority_queue<poi>qu;
qu.push({s,0});
dis[s]=0;
while(qu.size())
{
poi bef=qu.top();
qu.pop();
if(vis[bef.x]==1)
continue;
vis[bef.x]=1;
for(int i=h[bef.x];i!=-1;i=edge[i].ne)
{
int val=edge[i].val,to=edge[i].to;
if(dis[to]>val+bef.len)
{
dis[to]=val+bef.len;
qu.push({to,dis[to]});
}
}
}
return ;
}
void solve()
{
tot=0;
int u,v,w;
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
vv[i].clear();
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
scanf("%d%d%d%d",&k,&p,&s,&t);
dfs(1,0);
for(int i=1;i<=n;i++)
{//+2*n是进入,+n是出
add(i,d[i]+n,0);
add(d[i]+2*n,i,0);
if(d[i]-k>0)
add(d[i]+n,d[i]-k+2*n,p);
if(d[i]+k<=n)
add(d[i]+n,d[i]+k+2*n,p);
}
dij();
printf("%lld\n",dis[t]);
return ;
}
signed main()
{
int t;
scanf("%lld",&t);
while(t--)
solve();
return 0;
}
1010.Bragging Dice
题意:摇骰子游戏,每人n个骰子,摇出点数后.某一个人先手他会说某个数有几个.这个数量就是两个人该点数的骰子数量总和,如果后手不相信,可以开骰.如果数量符合那么后手失败,否则后手成功.
思路:喝酒玩这个,按照题目意思,两者都知道了对方骰子,那肯定先手会胜利,因为都已经互相知道了.但是有一个特殊规则,题目补充了,某个人每个骰子的点数都不一样,那么他的所有点数都为0.当两者都是这种情况,那么点数都为0,但是先手不能说0,所以后手必胜,其余先手必胜.
#include<bits/stdc++.h>
using namespace std;
map<int,int>m1,m2;
void solve()
{
m1.clear(),m2.clear();
int n,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&x),m1[x]++;
for(int i=1;i<=n;i++)
scanf("%d",&x),m2[x]++;
int f=0;
for(int i=1;i<=6;i++)
{
if(m1[i]>1||m2[i]>1)
f=1;
}
if(f==1)
printf("Win!\n");
else
printf("Just a game of chance.\n");
return;
}
signed main()
{
int t;
scanf("%d",&t);
while(t--)
solve();
return 0;
}
1012.Buy Figurines(线段树二分+优先队列)
题意:有n个人来m个水龙头处打水,已知每个人的来的时间和把水桶装满的时间,每个人都会优先选择人少的水龙头处排队.问最少要花费多久这些人把水桶接满.
思路:针对于这个问题,东北省赛做过类似的题.我们考虑如何才能最快的知道哪个水龙头此时的人最少.可以考虑用线段树维护一个当前区间内人最少有多少个在排队.每个人过来排队时去进行更新.首先在线段树进行查找,选取左右区间中最小值更小的那个区间进行深入查找,这样就可以找到人最少的那个水龙头,我们在对于进来接水的人用一个优先队列去维护,里面维护的值就是每个人的序号和接水结束的时间(按照时间排序的小顶堆).当每个新人来的时候我们把这个人来的时间内接完水的人全部清除(此时每个人要按照来的时间排序,从小到大,清除的话在线段树找到对应序号的单点-1即可),当把那些要走的人清除后再把新人加入优先队列.(ps:因为要考虑结束时间,所以要记录一下每个水龙头当前要在什么时候没有人.)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =2e5+10,mod=998244353;
int ent[N];
struct node
{
int minn,num,l,r;
}tr[4*N];//这棵树的含义是该水龙头有多少人,num为人数,minn为区间最小值
struct peo
{
int idx,time;
bool operator<(const peo b)const{
return this->time>b.time;
}
};
struct peo11
{
int st,len;
}peo1[N];
bool cmp(peo11 a,peo11 b)
{
if(a.st!=b.st)
return a.st<b.st;
else
return a.len<b.len;
}
priority_queue<peo>qu;
void build(int l,int r,int node)
{
tr[node].l=l;
tr[node].r=r;
tr[node].minn=0;
tr[node].num=0;
if(l==r)
return ;
int mid=(l+r)/2;
build(l,mid,node<<1);
build(mid+1,r,node<<1|1);
return ;
}
void modify(int node,int st,int en)
{
int l=tr[node].l,r=tr[node].r;
if(l==r)
{
tr[node].num++;
tr[node].minn=tr[node].num;
qu.push({l,(max(ent[l],st)+en)});
ent[l]=max(ent[l],st)+en;//更新当前水龙头什么时候没人
//需要注意的是下一个人可能在没人之后几分钟才来接水,也可能直接排在别人后面
//所以这里要取max
return ;
}
int mid=(l+r)/2;
if(tr[node<<1].minn<=tr[node<<1|1].minn)
modify(node<<1,st,en);
else
modify(node<<1|1,st,en);
tr[node].minn=min(tr[node<<1].minn,tr[node<<1|1].minn);
return ;
}
void update(int node,int idx)
{
int l=tr[node].l,r=tr[node].r;
if(l==r)
{
tr[node].num--;
tr[node].minn=tr[node].num;
return ;
}
int mid=(l+r)/2;
if(l<=idx&&idx<=mid)
update(node<<1,idx);
else if(mid<idx&&idx<=r)
update(node<<1|1,idx);
tr[node].minn=min(tr[node<<1].minn,tr[node<<1|1].minn);
}
void solve()
{
int n,m;
scanf("%lld%lld",&n,&m);//输入n,m,n个人和m个水龙头
build(1,m,1);//对于水龙头建树
for(int i=1;i<=m;i++)
ent[i]=0;
for(int i=1;i<=n;i++)
scanf("%lld%lld",&peo1[i].st,&peo1[i].len);
sort(peo1+1,peo1+1+n,cmp);
for(int i=1;i<=n;i++)
{
while(qu.size()&&qu.top().time<peo1[i].st)
{
update(1,qu.top().idx);
qu.pop();
}
modify(1,peo1[i].st,peo1[i].len);
}
while(qu.size())
{
update(1,qu.top().idx);
qu.pop();
}
int ans=-1;
for(int i=1;i<=m;i++)
{
ans=max(ans,ent[i]);
}
printf("%lld\n",ans);
return ;
}
signed main()
{
int t;
scanf("%lld",&t);
while(t--)
solve();
return 0;
}