[考试反思]1105csp-s模拟测试102: 贪婪

[考试反思]1105csp-s模拟测试102: 贪婪_第1张图片

还是有点蠢。。。

多测没清空T3挂40。。。(只得了人口普查分20)

多测题要把样例复制粘两遍自测一下防止未清空出锅。

然而不算分。。。

其实到现在了算不算也不重要了吧。。。

而且其实T3只考虑最长路上的点这个思路其实肯定都能想到啊。

懒得打。那么就分少啊。

对于分数,还是要贪婪一些的。

 

T1:你相信引力吗

维护最大值关系:肉眼可见的单调栈/队列。(刚开始认为是栈,后来其实发现是一个队列)

环,经典套路,拆成两倍长的序列。

维护一个单调不增的单调栈,根据题目含义画画图,可以发现答案是:

对于当前点,从栈里第一个严格大于当前元素的值开始数,栈里有多少个元素。

暴力思路就是二分。

可以发现你找到的那个元素后面的部分都会被弹掉,所以一边弹一边计数即可。

特殊的一点是权值相同的,这部分不会被弹栈,但是对答案也有贡献。

所以在每个元素入栈的时候记录一下栈顶有几个连续相同的元素即可。

然后还要考虑找到第一个比当前元素大的元素,所以检查一下栈底的元素是否比当前元素大,如果大的话再累加1个答案。

然后还要考虑环拉成序列以后同一个位置的元素可能在栈里出现2次。

所以就不是栈了,是个队列。如果队首元素的下表就是当前的下标,那么就弹出。

弹出的时候要考虑它对“栈里连续相同元素数量”的影响。

还有一个问题,如果对于一对冰锥,它的优弧和劣弧都满足条件,那么它的答案会被计算两次。

如果最大值出现了k次,那么它对答案产生的多出的贡献就是$\frac{k(k-1)}{2}$

特别的,当k=1时,如果次大值出现了j次,那么多出的答案就是$j$

所以把多出的贡献减去就是最终答案。

细节较多。但是如果上述分类讨论都注意到了的话调试还是挺简单的。

 1 #include
 2 int read(){
 3     register int p=0;register char ch=getchar();
 4     while(ch<'0'||ch>'9')ch=getchar();
 5     while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar();
 6     return p;
 7 }
 8 int n,a[5000005],q[10000005],p[10000005],s[10000005],h=1,t,mx,se,tmx,tse;long long ans;
 9 int main(){
10     freopen("jolyne.in","r",stdin);freopen("jolyne.out","w",stdout);
11     n=read();
12     for(int i=1;i<=n;++i)a[i]=read();
13     for(int i=1;i<=n;++i)
14         if(a[i]>mx)se=mx,tse=tmx,mx=a[i],tmx=1;
15         else if(a[i]==mx)tmx++;
16         else if(a[i]>se)se=a[i],tse=1;
17         else if(a[i]==se)tse++;
18     for(int i=1;i<=n;++i){
19         while(t>=h&&a[i]>q[t])t--;
20         if(t>=h&&q[t]==a[i])s[++t]=s[t-1]+1;else s[++t]=1;
21         q[t]=a[i];p[t]=i;
22     }
23     for(int i=1;i<=n;++i){int nt=0;
24         while(t>=h&&p[h]<=i)nt=a[i]==q[h],h++;
25         while(t>=h&&a[i]>q[t])t--,ans++;
26         if(t>=h&&q[t]==a[i])s[++t]=s[t-1]+1;else s[++t]=1;
27         q[t]=a[i];p[t]=n+i;
28         if(h==t||q[h]!=q[t])nt=0;
29         s[t]-=nt;ans+=s[t]-1;if(q[h]!=a[i])ans++;
30 //        for(int j=h;j<=t;++j)printf("%3d ",q[j]);puts("");
31 //        for(int j=h;j<=t;++j)printf("%3d ",s[j]);puts("");
32 //        printf("%lld\n",ans);
33     }
34     if(tmx==1)ans-=tse;
35     else ans-=tmx*(tmx-1ll)/2;
36     printf("%lld\n",ans);
37 }
View Code

 

T2:停不下来的团长奥尔加

比较明显的线性递推。

可以根据每个点的出度/入度平衡来做。

出度分为去右边的和去左边的,入度也是。

然后就可以递推了。

 1 #include
 2 #define mod 1000000007
 3 int n,to[1000005];long long ans,oR[1000005],oL[1000005],iL[1000005],iR[1000005];
 4 int main(){
 5     freopen("rideon.in","r",stdin);
 6     freopen("rideon.out","w",stdout);
 7     scanf("%d",&n);
 8     for(int i=1;i<=n;++i)scanf("%d",&to[i]);
 9     iL[n+1]=1;
10     for(int i=n;i;--i){
11         oR[i]=iL[i+1];
12         oL[i]=oR[i];
13         iR[to[i]]=(iR[to[i]]+oL[i])%mod;
14         iL[i]=(oR[i]+oL[i]-iR[i]+mod)%mod;
15         ans=(ans+iL[i]+iR[i])%mod;
16     }printf("%lld\n",ans);
17 }
View Code

 

T3:Lesson5!

这题为什么要多测啊啊啊?一定要记得清空不然调到死。

建超级源汇会好做一些。(不建也可以,区别不大)

先预处理出来源点到每个点的距离f,以及每个点到汇点的距离g。

正反拓扑得到。

然后对于每一条边,经过它的最长路是fx+gy+1。称之为边的权值。

对于删除一个点,那么所有与它有关的边都不能贡献答案。

所以再一个正向拓扑,分层考虑所有点。

对于每一个点,删除所有入边的权值的贡献(就是从数据结构里删除),此时出边的贡献还没有统计。

所谓的贡献答案,其实就是在所有可行的最长路中取出最大的那一个,是一个维护了多个最长路决策的数据结构。

(所以说题解说的好像多么玄乎一样)

先不考虑这个数据结构是什么,假设我们能应付它需要的所有操作。

所以其实当前的状态下,就相当与删除了这个点时的状态,所以此时的最长路就是删掉这个点的最长路。(就是从数据结构里找到最大值)

这个点考虑完之后就可以把它的出边都贡献答案了。(就是放入数据结构)

因为你会在进入这一层的时候删除,走出这一层的时候加入,那么其实它只会在本层到下一层的时间存在。

进入下一层的时候代表这条最长路的边就不再是这一条了而是在这条最长路上的下一条。所以一定会不重不漏的最优决策。

怎么求出答案?需要一个可以求最大值,可以删除的数据结构。

multiset。(于是我开始嘲笑出题人不会STL还要写权值线段树)

然后我被卡常了。。。

但是我坚决不打数据结构!所以就上priority_queue乱搞。

加一个懒惰删除即可。

不建立超级源汇的话,根据含义考虑,其实少的边就是从每一个点到其汇点的贡献,所以在拓扑开始之前直接把每个点到它的汇点的答案放进数据结构里就好了。

还有我感觉mikufun的思路很好,附个链接(虽然skyh没脸又爆跳父亲AC了然后才开始长脸重新改了一遍(再长脸脸该多大了啊。。。))

 1 #include
 2 using namespace std;
 3 priority_queue<int>Q;
 4 vector<int>v[100005];
 5 int n,m,dp[100005],ans,ansp,cnt,fir[100005],l[700005],to[700005],deg[100005];
 6 int f[100005],g[100005],q[100005],X[700005],Y[700005],delcnt[100005];
 7 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;deg[b]++;}
 8 int main(){
 9     freopen("johnny.in","r",stdin);
10     freopen("johnny.out","w",stdout);
11     int T;scanf("%d",&T);
12     while(T--){
13         scanf("%d%d",&n,&m);
14         for(int i=0;i<=n+1;++i)fir[i]=f[i]=g[i]=0,v[i].clear();
15         while(!Q.empty())Q.pop();
16         ans=1234567890;cnt=0;
17         for(int i=1;i<=m;++i)scanf("%d%d",&X[i],&Y[i]),link(X[i],Y[i]);
18         for(int i=m+1;i<=m+n;++i)X[i]=0,Y[i]=i-m,link(X[i],Y[i]);
19         m+=n;
20         for(int i=m+1;i<=m+n;++i)X[i]=i-m,Y[i]=n+1,link(X[i],Y[i]);
21         m+=n;n++;
22         int t=0;
23         for(int i=0;i<=n;++i)if(!deg[i])q[++t]=i;
24         for(int h=1;h<=t;++h){
25             int p=q[h];
26             for(int i=fir[p];i;i=l[i]){
27                 deg[to[i]]--;f[to[i]]=max(f[to[i]],f[p]+1);
28                 if(!deg[to[i]])q[++t]=to[i];
29             }
30         }
31         t=0;for(int i=0;i<=n;++i)fir[i]=0;cnt=0;
32         for(int i=1;i<=m;++i)link(Y[i],X[i]);
33         for(int i=0;i<=n;++i)if(!deg[i])q[++t]=i;
34         for(int h=1;h<=t;++h){
35             int p=q[h];
36             for(int i=fir[p];i;i=l[i]){
37                 deg[to[i]]--;g[to[i]]=max(g[to[i]],g[p]+1);
38                 if(!deg[to[i]])q[++t]=to[i];
39             }
40         }
41         t=0;for(int i=0;i<=n;++i)fir[i]=0;cnt=0;
42         for(int i=1;i<=m;++i)link(X[i],Y[i]),v[Y[i]].push_back(1+g[Y[i]]+f[X[i]]);
43         for(int i=0;i<=n;++i)if(!deg[i])q[++t]=i;
44         Q.push(2);
45         for(int h=1;h<=t;++h){
46             int p=q[h];
47             for(int i=0;i;
48             while(delcnt[Q.top()])delcnt[Q.top()]--,Q.pop();
49             if(Q.top()p;
50             if(Q.top()==ans&&pp;
51             for(int i=fir[p];i;i=l[i]){
52                 deg[to[i]]--;Q.push(1+f[p]+g[to[i]]);
53                 if(!deg[to[i]])q[++t]=to[i];
54             }
55         }
56         printf("%d %d\n",ansp,ans-2);
57     }
58 }
60M的读入量没写快读居然也过了。。。

你可能感兴趣的:([考试反思]1105csp-s模拟测试102: 贪婪)