P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm(Tarjan+记忆化)

P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm

题意翻译

题目描述

每年,在威斯康星州,奶牛们都会穿上衣服,收集农夫约翰在N(1<=N<=100,000)个牛棚隔间中留下的糖果,以此来庆祝美国秋天的万圣节。

由于牛棚不太大,FJ通过指定奶牛必须遵循的穿越路线来确保奶牛的乐趣。为了实现这个让奶牛在牛棚里来回穿梭的方案,FJ在第i号隔间上张贴了一个“下一个隔间”Next_i(1<=Next_i<=N),告诉奶牛要去的下一个隔间;这样,为了收集它们的糖果,奶牛就会在牛棚里来回穿梭了。

FJ命令奶牛i应该从i号隔间开始收集糖果。如果一只奶牛回到某一个她已经去过的隔间,她就会停止收集糖果。

在被迫停止收集糖果之前,计算一下每头奶牛要前往的隔间数(包含起点)。

输入格式

第1行 整数n。

第2行到n+1行 每行包含一个整数 next_i 。

输出格式

n行,第i行包含一个整数,表示第i只奶牛要前往的隔间数。

样例解释

有4个隔间

隔间1要求牛到隔间1

隔间2要求牛到隔间3

隔间3要求牛到隔间2

隔间4要求牛到隔间3

牛1,从1号隔间出发,总共访问1个隔间;

牛2,从2号隔间出发,然后到三号隔间,然后到2号隔间,终止,总共访问2个隔间;

牛3,从3号隔间出发,然后到2号隔间,然后到3号隔间,终止,总共访问2个隔间;

牛4,从4号隔间出发,然后到3号隔间,然后到2号隔间,然后到3号隔间,终止,总共访问3个隔间。

翻译提供者:吃葡萄吐糖

题目描述

Every year in Wisconsin the cows celebrate the USA autumn holiday of Halloween by dressing up in costumes and collecting candy that Farmer John leaves in the N (1 <= N <= 100,000) stalls conveniently numbered 1..N.

Because the barn is not so large, FJ makes sure the cows extend their fun by specifying a traversal route the cows must follow. To implement this scheme for traveling back and forth through the barn, FJ has posted a 'next stall number' next_i (1 <= next_i <= N) on stall i that tells the cows which stall to visit next; the cows thus might travel the length of the barn many times in order to collect their candy.

FJ mandates that cow i should start collecting candy at stall i. A cow stops her candy collection if she arrives back at any stall she has already visited.

Calculate the number of unique stalls each cow visits before being forced to stop her candy collection.

POINTS: 100

每年万圣节,威斯康星的奶牛们都要打扮一番,出门在农场的N个牛棚里转 悠,来采集糖果.她们每走到一个未曾经过的牛棚,就会采集这个棚里的1颗糖果.

农场不大,所以约翰要想尽法子让奶牛们得到快乐.他给每一个牛棚设置了一个“后继牛 棚”.牛棚i的后继牛棚是next_i 他告诉奶牛们,她们到了一个牛棚之后,只要再往后继牛棚走去, 就可以搜集到很多糖果.事实上这是一种有点欺骗意味的手段,来节约他的糖果.

第i只奶牛从牛棚i开始她的旅程.请你计算,每一只奶牛可以采集到多少糖果.

输入输出格式

输入格式:

 

* Line 1: A single integer: N

* Lines 2..N+1: Line i+1 contains a single integer: next_i

 

输出格式:

 

* Lines 1..N: Line i contains a single integer that is the total number of unique stalls visited by cow i before she returns to a stall she has previously visited.

 

输入输出样例

输入样例#1: 复制
4 
1 
3 
2 
3 
输出样例#1: 复制
1 
2 
2 
3 

说明

Four stalls.

* Stall 1 directs the cow back to stall 1.

* Stall 2 directs the cow to stall 3

* Stall 3 directs the cow to stall 2

* Stall 4 directs the cow to stall 3

Cow 1: Start at 1, next is 1. Total stalls visited: 1.

Cow 2: Start at 2, next is 3, next is 2. Total stalls visited: 2. Cow 3: Start at 3, next is 2, next is 3. Total stalls visited: 2. Cow 4: Start at 4, next is 3, next is 2, next is 3. Total stalls visited: 3

 

#include<bits/stdc++.h>

#define N 100007

using namespace std;
int n,m,ans,cnt,num,tot;
int head[N],Head[N],dfn[N],low[N],scc[N],bel[N];
int V[N],A[N];
bool in_st[N],vis[N];
stack<int>st;
struct edge{
    int u,v,nxt;
}e[N<<1],E[N<<1];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

inline void add(int u,int v)
{
    e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;
}

inline void add_(int u,int v)
{
    E[++cnt].v=v;E[cnt].nxt=Head[u];Head[u]=cnt;
}

void Tarjan(int u)
{
    dfn[u]=low[u]=++cnt;
    st.push(u);in_st[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
            Tarjan(v),low[u]=min(low[u],low[v]);
        else if(in_st[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        num++;tot=0;
        while(st.top()!=u)
        {
            tot++;
            bel[st.top()]=num;
            in_st[st.top()]=0;st.pop();
        }
        tot++;
        bel[st.top()]=num;scc[num]=tot;
        in_st[st.top()]=0;st.pop();
    }
}

void dfs(int u,int sum)
{
    ans=sum;
    for(int i=Head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(vis[v]) continue;
        vis[v]=1;dfs(v,sum+scc[v]);
    }
}

int main()
{
    int x;
    n=read();
    for(int i=1;i<=n;i++)
    {
        x=read();add(i,x);
    }
    cnt=0;
    for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
    cnt=0;
    for(int i=1;i<=n;i++) for(int j=head[i];j;j=e[j].nxt)
        if(bel[i]!=bel[e[j].v]) add_(bel[i],bel[e[j].v]);
        
    for(int i=1;i<=n;i++)
    {    
        ans=0;memset(vis,0,sizeof vis);
        if(!V[bel[i]])
        {
            V[bel[i]]=1,vis[bel[i]]=1,dfs(bel[i],scc[bel[i]]),A[bel[i]]=ans;
        }
        else ans=A[bel[i]];
        printf("%d\n",ans);
    }
    return 0;
} 
40暴搜
/*
Tarjan处理出环之后记忆化搜索即可。 
*/
#include<bits/stdc++.h>

#define N 100007

using namespace std;
int n,m,ans,cnt,num,tot;
int head[N],Head[N],dfn[N],low[N],scc[N],bel[N];
int V[N],A[N];
bool in_st[N],vis[N];
stack<int>st;
struct edge{
    int u,v,nxt;
}e[N<<1],E[N<<1];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

inline void add(int u,int v)
{
    e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;
}

inline void add_(int u,int v)
{
    E[++cnt].v=v;E[cnt].nxt=Head[u];Head[u]=cnt;
}

void Tarjan(int u)
{
    dfn[u]=low[u]=++cnt;
    st.push(u);in_st[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
            Tarjan(v),low[u]=min(low[u],low[v]);
        else if(in_st[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        num++;tot=0;
        while(st.top()!=u)
        {
            tot++;
            bel[st.top()]=num;
            in_st[st.top()]=0;st.pop();
        }
        tot++;
        bel[st.top()]=num;scc[num]=tot;
        in_st[st.top()]=0;st.pop();
    }
}

void dfs(int u,int sum)
{
    ans=sum;
    for(int i=Head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(vis[v]) continue;
        vis[v]=1;dfs(v,sum+scc[v]);
    }
}

int main()
{
    int x;
    n=read();
    for(int i=1;i<=n;i++)
    {
        x=read();add(i,x);
    }
    cnt=0;
    for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
    cnt=0;
    for(int i=1;i<=n;i++) for(int j=head[i];j;j=e[j].nxt)
        if(bel[i]!=bel[e[j].v]) add_(bel[i],bel[e[j].v]);
        
    for(int i=1;i<=n;i++)
    {    
        ans=0;memset(vis,0,sizeof vis);
        if(!V[bel[i]])
        {
            V[bel[i]]=1,vis[bel[i]]=1,dfs(bel[i],scc[bel[i]]),A[bel[i]]=ans;
        }
        else ans=A[bel[i]];
        printf("%d\n",ans);
    }
    return 0;
} 

 

转载于:https://www.cnblogs.com/L-Memory/p/9878738.html

以下是P4087 [USACO17DEC]Milk Measurement的c++代码: ```c++ #include<bits/stdc++.h> using namespace std; int n,d,i,x,minn=1e9,maxn=-1e9,sum=7;//注意sum要初始为7,因为一开始有三个人挤奶! map<int,int> mp; struct node{ int day,milk,id;//day表示某一天,milk表示这一天的产奶量,id表示这头牛的编号 }a[100010]; bool cmp(node x,node y){ return x.day<y.day; } int main(){ scanf("%d%d",&n,&d); for(i=1;i<=n;i++){ scanf("%d%d%d",&a[i].day,&a[i].id,&a[i].milk); minn=min(minn,a[i].id);//记录最小的牛的编号 maxn=max(maxn,a[i].id);//记录最大的牛的编号 } sort(a+1,a+n+1,cmp);//排序 for(i=1;i<=n;i++){ int p=a[i].id; mp[p]+=a[i].milk;//记录每头牛产奶总量 if(mp[p]-a[i].milk>=mp[minn]&&mp[p]>=mp[minn]){//如果这头牛的产奶总量减去这一天的产奶量后等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 sum--; } if(mp[p]>=mp[maxn]&&mp[p]-a[i].milk<mp[maxn]){//如果这头牛的产奶总量大于等于最大产奶量且这头牛的产奶总量减去这一天的产奶量小于最大产奶量 sum++; } if(mp[p]-a[i].milk<mp[maxn]&&mp[p]>=mp[maxn]){//如果这头牛的产奶总量减去这一天的产奶量小于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]-mp[p]+a[i].milk>0)sum++; } mp[p]-=a[i].milk;//减去这一天的产奶量 if(i==n||a[i].day!=a[i+1].day){//如果到了新的一天或者到了最后一天 if(mp[maxn]!=mp[a[i].id]&&mp[a[i].id]>=mp[maxn])sum++;//如果这头牛的产奶总量不等于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]==mp[a[i].id]){//如果这头牛的产奶总量等于最大产奶量 if(a[i].id==maxn)sum+=0;//如果这头牛就是最大产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } if(mp[minn]!=mp[a[i].id]&&mp[a[i].id]>=mp[minn])sum++;//如果这头牛的产奶总量不等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 if(mp[minn]==mp[a[i].id]){ if(a[i].id==minn)sum+=0;//如果这头牛就是最小产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } } } printf("%d\n",sum); return 0; } ``` 该题的解题思路是模拟,需要注意细节问题。我们可以首先将输入的数据按天数排序,然后模拟每一天挤奶的情况,并根据题目要求进行计数即可。具体细节请见代码注释。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值