2018.8.19提高B组模拟考试
嗯明天要开始做A组了,因为B组全是原题...题还特别水...
T1 题意简述:jzoj3927
解题思路:各位大佬有没有觉得这道题很眼熟呀?
没错,这道题和[SDOI2008]仪仗队一模一样...
做过的大佬可以跳过了...
画图观察发现答案即为sum[phi[1]~phi[n-1]]+2。
注意当n=1时要特判。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define ll long long using namespace std; ll n,jdg[100001],prime[100001],phi[100001],cnt,ans; void getphi() { phi[1]=1;jdg[1]=1; for(ll i=2;i<=n;i++) { if(!jdg[i]) { prime[++cnt]=i; phi[i]=i-1; } for(ll j=1;j<=cnt;j++) { if(i*prime[j]>n) break; jdg[i*prime[j]]=1; if(i%prime[j]==0) { phi[i*prime[j]]=phi[i]*prime[j]; break; } phi[i*prime[j]]=phi[i]*(prime[j]-1); } } } int main() { scanf("%lld",&n); if(n==1){printf("0\n");return 0;} getphi(); for(int i=1;i<n;i++) ans+=phi[i]; ans=ans*2+1; printf("%lld\n",ans); }
T2 题意简述:jzoj3928
解题思路:纯贪心。
把事件按时间从早到晚排序,用一个小根堆维护。
若队列内元素个数小于当前事件时间,则直接加入队列。
否则,把队列中价值最低的元素与当前事件价值对比。
若当前事件价值较大就把堆顶弹出,压入当前事件。
注意特判价值为负以及时间为0的情况。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #define ll long long using namespace std; ll n,ans; struct uio{ ll tim,val; }win[200001]; priority_queue<ll,vector<ll>,greater<ll> > que; bool cmp(uio x,uio y) { if(x.tim==y.tim) return x.val>y.val; return x.tim<y.tim; } int main() { scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%lld%lld",&win[i].tim,&win[i].val); sort(win+1,win+1+n,cmp); for(ll i=1;i<=n;i++) { if(win[i].val<0||win[i].tim==0) continue; if(que.size()<win[i].tim){que.push(win[i].val);continue;} else if(que.top()<win[i].val){que.pop();que.push(win[i].val);continue;} } while(!que.empty()) { ans+=que.top(); que.pop(); } printf("%lld\n",ans); return 0; }
T3 题意简述:jzoj3929
解题思路:题解中提供了2种思路。这里介绍(懒人专属)贪心做法。
发现题目中给的图是基环森林(树),因此可以贪心。
类似拓扑排序,找出图中所有入度为0的点,压入队列中。
对于一个点,查询它和它的儿子是否已经计入答案。若均未计入答案则儿子计入答案。
查询当前点的儿子的儿子是否入度为0,若是则压入队列。
最后会剩下一个某些点已被染色的环,按照以上策略再次贪心即可。
另一种做法是dp,想看这种解法的可以去PoPoQQQ大佬那里看看。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> using namespace std; int n,ans,son[1000001],head[1000001],d[1000001],vis[1000001]; queue<int> que; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) {scanf("%d",&son[i]);d[son[i]]++;} for(int i=1;i<=n;i++) if(!d[i]) que.push(i); while(!que.empty()) { int now=que.front();que.pop(); if(!vis[now]&&!vis[son[now]]) { vis[son[now]]=1,ans++; d[son[son[now]]]--; if(!d[son[son[now]]]) que.push(son[son[now]]); } vis[now]=1; } for(int i=1;i<=n;i++) if(!vis[i]) { vis[i]=1; int tmp=son[i],cnt=1; while(tmp!=i) vis[tmp]=1,tmp=son[tmp],cnt++; ans+=cnt/2; } printf("%d\n",ans); return 0; }