是题比较简单的一次,虽然因为CE爆了一道题...
谨记:交代码之前 一定要编译 一定要编译 一定要编译
题目:nkoj补题专场(高二)N-Q
T1:自描述序列
暴力模拟即可,没什么好说的
#include<bits/stdc++.h> using namespace std; #define N 10000005 int a[N]; int main(){ a[1]=a[4]=a[5]=1;a[2]=a[3]=2; for(int i=6,j=4;i<=10000000;i++){ if(a[j]==1) a[i]=3-a[i-1],j++; else a[i]=3-a[i-1],a[i+1]=a[i],i++,j++; }int T;scanf("%d",&T); while(T--){ int n;scanf("%d",&n); printf("%d\n",a[n]); }return 0; }
T2:极限
极限的情况下显然易想到找环;
所以根据输入建图,Tarjan缩点找环后拓扑处理即可;
#include<bits/stdc++.h> using namespace std; #define N 100005 #define ll long long int n,f[N],dfn[N],low[N],ed[N],tot,cnt,scc,bel[N],ins[N],ind[N],g[N]; ll sum[N],sz[N];int tp[N];stack<int>s;queue<int>q; inline ll gcd(ll x,ll y){return y?gcd(y,x%y):x;} void Tarjan(int x){ dfn[x]=low[x]=++cnt; ins[x]=1;s.push(x); if(!dfn[f[x]]){ Tarjan(f[x]); low[x]=min(low[x],low[f[x]]); }else if(ins[f[x]]) low[x]=min(low[x],low[f[x]]); if(dfn[x]==low[x]){ int y;++scc; do{y=s.top();s.pop();ins[y]=0;bel[y]=scc;sz[scc]++;sum[scc]+=f[y];}while(y^x); } }int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",f+i); for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i); for(int i=1;i<=n;i++){ if(bel[f[i]]==bel[i]) continue; ind[bel[f[i]]]++;ed[bel[i]]=bel[f[i]]; }for(int i=1;i<=scc;i++) if(!ind[i]) q.push(i); for(int i=1;i<=scc;i++) g[i]=i; while(!q.empty()){ int u=q.front();q.pop();tp[++tot]=u; if(!(--ind[ed[u]])) q.push(ed[u]); }for(int i=tot;i;i--){ if(ed[tp[i]]) g[tp[i]]=g[ed[tp[i]]]; else{ ll t=gcd(sz[tp[i]],sum[tp[i]]); sz[tp[i]]/=t;sum[tp[i]]/=t; } }for(int i=1;i<=n;i++){ printf("%lld/%lld\n",sum[g[bel[i]]],sz[g[bel[i]]]); }return 0; }
T3:最大三角形
给定一些长度求最大周长三角形的周长,贪心策略显然是取最大三条,若不行则将最大换位第四大,以此类推;
故原题可转化为求静态区间第k大:主席树;
考虑复杂度:貌似是个q*n*log2(n)的东西;
但考虑贪心选择时判断不行的条件:a+b>c;
显然这样迭代a、b、c的缩小速度将比除以二更快;
故实际复杂度应为q*log2(n)*log2(n),可过;
#include<bits/stdc++.h> using namespace std; #define N 200005 #define ll long long int n,q,a[N],b[N],rt[N],cnt[N<<5],tot,ls[N<<5],rs[N<<5]; void build(int &p,int q,int l,int r,int x){ p=++tot;ls[p]=ls[q];rs[p]=rs[q];cnt[p]=cnt[q]+1; if(l==r) return;int mid=(l+r)>>1; if(x<=mid) build(ls[p],ls[q],l,mid,x); else build(rs[p],rs[q],mid+1,r,x); }int query(int p,int q,int l,int r,int k){ if(l==r){return cnt[q]-cnt[p]>=k?l:-1;} int mid=(l+r)>>1; if(k<=cnt[rs[q]]-cnt[rs[p]]) return query(rs[p],rs[q],mid+1,r,k); else return query(ls[p],ls[q],l,mid,k-(cnt[rs[q]]-cnt[rs[p]])); }int main(){ scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",a+i),b[i]=a[i]; sort(b+1,b+1+n);int t=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+t,a[i])-b; for(int i=1;i<=n;i++) build(rt[i],rt[i-1],1,t,a[i]); for(int i=1;i<=q;i++){ int l,r;scanf("%d%d",&l,&r);l--; if(r-l<3){puts("-1");continue;} ll mx=b[query(rt[l],rt[r],1,t,1)]; ll mid=b[query(rt[l],rt[r],1,t,2)]; bool flag=0; for(int j=3;j<=r-l;j++){ ll mn=b[query(rt[l],rt[r],1,t,j)]; if(mid+mn>mx){ printf("%lld\n",mx+mid+mn); flag=1;break; }else mx=mid,mid=mn; }if(!flag) puts("-1"); }return 0; }
T4:彩色的树
树形dp是显然的;但是正向统计并不好处理;
故考虑简单容斥一下,求每种颜色不能覆盖的路径数量;
显然各种颜色会将树划分为多个连通块,每个连通块内的节点之间的路径就是不会被该颜色覆盖的路径;
故直接树形dp,拿个桶搞一搞就行了
#include<bits/stdc++.h> using namespace std; #define N 200005 #define ll long long int n,a[N],lst[N],ed[N<<1],nxt[N<<1],tot,vis[N];ll cnt[N],sum,sz[N]; inline void add_e(int x,int y){ed[++tot]=y,nxt[tot]=lst[x],lst[x]=tot;} void dfs(int x,int fa){ sz[x]=1;int pre=cnt[a[x]]; for(int i=lst[x];i;i=nxt[i]){ if(ed[i]==fa) continue; dfs(ed[i],x);sz[x]+=sz[ed[i]]; sum+=1ll*(sz[ed[i]]-cnt[a[x]]+pre)*(sz[ed[i]]-cnt[a[x]]+pre-1)/2; cnt[a[x]]=pre; }cnt[a[x]]+=sz[x]; }int main(){ scanf("%d",&n);int tp=0; for(int i=1;i<=n;i++){ scanf("%d",a+i); tp+=(!vis[a[i]]);vis[a[i]]=1; }for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); add_e(x,y);add_e(y,x); }dfs(1,0); for(int i=1;i<=n;i++){ if(!vis[i]) continue; sum+=1ll*(n-cnt[i])*(n-cnt[i]-1)/2; }printf("%lld",1ll*n*(n-1)/2*tp-sum); return 0; }