是题比较简单的一次,虽然因为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;
}
View Code

  

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;
}
View Code

 

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;
}
View Code

 

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;
}
View Code

  

posted on 2021-10-14 20:38  Aurora-Sley  阅读(29)  评论(0编辑  收藏  举报



Live2D