Comet OJ - Contest #14題解

Contest14的本質:區間覆蓋+Tarjan(ios

A

把距離公式兩邊平方便可數組

注意要long long優化

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

int x[100001];
int y[100001];
int n,i,j,k,l,X,Y,R,ans;

int main()
{
//  freopen("a.in","r",stdin);
    
    scanf("%d%d%d%d",&n,&X,&Y,&R);
    fo(i,1,n)
    scanf("%d%d",&x[i],&y[i]);
    
    fo(i,1,n)
    if (((long long)(x[i]-X)*(x[i]-X)+(long long)(y[i]-Y)*(y[i]-Y))<=(long long)R*R)
    ++ans;
    
    printf("%d\n",ans);
}

B

顯然p和k是位置最靠邊的兩個spa

考慮一下i和n的位置(靠左/靠右/左右)code

code

比較醜排序

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
using namespace std;

char a[1000001];
int T,n,i,j,k,l,ans,s1,s2,s3,s4;
char ch;

int main()
{
//  freopen("b.in","r",stdin);
    
    scanf("%d",&T);
    for (;T;--T)
    {
        ans=-1;
        scanf("%d",&n);
        
        fo(i,1,n)
        {
            ch=getchar();
            while (ch<'a' || ch>'z')
            ch=getchar();
            
            a[i]=ch;
        }
        
        fo(i,1,n)
        if (a[i]=='p')
        break;
        
        fd(j,n,1)
        if (a[j]=='k')
        break;
        
        fo(k,i+1,n)
        if (a[k]=='i')
        break;
        
        fo(l,k+1,n)
        if (a[l]=='n')
        break;
        if (i>=1 && i<k && k<l && l<j && j<=n && a[i]=='p' && a[k]=='i' && a[l]=='n' && a[j]=='k')
        ans=max(ans,max(max(k-i-1,l-k-1),j-l-1));
        
        fd(l,j-1,k+1)
        if (a[l]=='n')
        break;
        if (i>=1 && i<k && k<l && l<j && j<=n && a[i]=='p' && a[k]=='i' && a[l]=='n' && a[j]=='k')
        ans=max(ans,max(max(k-i-1,l-k-1),j-l-1));
        
        fd(l,j-1,1)
        if (a[l]=='n')
        break;
        fd(k,l-1,i+1)
        if (a[k]=='i')
        break;
        if (i>=1 && i<k && k<l && l<j && j<=n && a[i]=='p' && a[k]=='i' && a[l]=='n' && a[j]=='k')
        ans=max(ans,max(max(k-i-1,l-k-1),j-l-1));
        
        printf("%d\n",ans);
    }
}

C

區間覆蓋*1get

一開始覺得一對區間只能算一次答案string

維護斷點,那麼一種方案=斷點數-1(加上首尾)it

一次覆蓋後,區間外的不變,區間內的變爲0,邊界變爲1io

因此能夠合在一塊兒維護,區間外的*2,區間內的不變,邊界+2^(i-1)

i的答案爲斷點總數-2^i

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 20050321
using namespace std;

long long p[2001];
int L[2001];
int R[2001];
bool b[2001];
long long sum[2001];
long long Sum[2001];
int n,m,i,j,k,l;
long long ans;

int main()
{
//  freopen("c.in","r",stdin);
    
    scanf("%d%d",&n,&m);
    p[0]=1;
    fo(i,1,m)
    {
        scanf("%d%d",&L[i],&R[i]);
        p[i]=p[i-1]*2%mod;
    }
    
    sum[0]=sum[n]=1;
    fo(i,1,m)
    {
        fo(j,0,n) Sum[j]=sum[j];
        fo(j,0,L[i]-2) Sum[j]=(Sum[j]+sum[j])%mod;
        fo(j,R[i]+1,n) Sum[j]=(Sum[j]+sum[j])%mod;
        Sum[L[i]-1]=(Sum[L[i]-1]+p[i-1])%mod;
        Sum[R[i]]=(Sum[R[i]]+p[i-1])%mod;
        
        ans=0;
        fo(j,0,n)
        sum[j]=Sum[j],Sum[j]=0,ans=(ans+sum[j])%mod;
        
        printf("%lld\n",(ans-p[i]+mod)%mod);
    }
}

D

區間覆蓋*2

套路,詢問若一段操做的結果就把詢問離線按r排序,每次加一個操做計算答案

維護每一段區間的兩個端點(左右括號),那麼每加一個區間最多加4個括號,每一個括號只會被刪一次

每加一個區間,就至關於把中間的端點刪掉(大的能覆蓋掉小的),再在兩邊加上新的端點

詢問就是求每一個修改所剩餘的個數*權值的後綴和

用set維護括號,每次斷開後找中間的區間,用樹狀數組維護答案

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <set>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define low(x) (x&-(x))
using namespace std;

struct qs{
    int l,r,id;
} q[500001];
struct type{
    int x,s,t; //0=right 1=left
    bool friend operator < (type a,type b) {return a.x<b.x || a.x==b.x && a.t<b.t;}
};
int a[500001][3];
long long ans[500001];
long long tr[500001];
multiset<type> st;
multiset<type> :: iterator I,J;
int n,m,Q,i,j,k,l;

bool cmp(qs a,qs b)
{
    return a.r<b.r;
}

void change(int t,long long s)
{
    while (t<=n)
    {
        tr[t]+=s;
        t+=low(t);
    }
}
long long find(int t)
{
    long long ans=0;
    
    while (t)
    {
        ans+=tr[t];
        t-=low(t);
    }
    
    return ans;
}

int main()
{
//  freopen("d.in","r",stdin);
    
    scanf("%d%d%d",&n,&m,&Q);
    fo(i,1,n)
    scanf("%d%d%d",&a[i][0],&a[i][1],&a[i][2]);
    fo(i,1,Q)
    scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    
    sort(q+1,q+Q+1,cmp);
    
    st.insert({0,0,1});
    st.insert({m,0,0});
    fo(i,1,Q)
    {
        fo(j,q[i-1].r+1,q[i].r)
        {
            I=st.lower_bound({a[j][0]-1,0,1});
            if (I!=st.begin())
            {
                --I;
                if ((*I).x<a[j][0]-1)
                {
                    k=(*I).s;
                    st.insert({a[j][0]-1,k,0});
                    st.insert({a[j][0]-1,k,1});
                }
            }
            
            I=st.upper_bound({a[j][1],0,0});
            if (I!=st.end())
            {
                if (a[j][1]<(*I).x)
                {
                    k=(*I).s;
                    st.insert({a[j][1],k,0});
                    st.insert({a[j][1],k,1});
                }
            }
            
            I=st.lower_bound({a[j][0]-1,0,1});
            J=st.upper_bound({(*I).x,0,1});
            while (I!=st.end() && (*I).x<a[j][1])
            {
                if ((*I).s)
                change((*I).s,-(long long)a[(*I).s][2]*((*J).x-(*I).x));
                
                st.erase(I);
                J=st.lower_bound({a[j][0]-1,0,1});
                st.erase(J);
                
                I=st.lower_bound({a[j][0]-1,0,1});
                J=st.upper_bound({(*I).x,0,1});
            }
            
            st.insert({a[j][0]-1,j,1});
            st.insert({a[j][1],j,0});
            change(j,(long long)a[j][2]*(a[j][1]-a[j][0]+1));
        }
        
        ans[q[i].id]=find(q[i].r)-find(q[i].l-1);
    }
    
    fo(i,1,Q)
    printf("%lld\n",ans[i]);
}

E

Tarjan*1

這應該是除了那道簡單數論之外最水的E了

Tarjan縮強聯通份量,dp維護從起點到每一個點的最小邊權、最大邊權、最大差值

正確性:最大差值與路徑上的最大&最小值有關,那麼必定會在找到後面那個(大or小)的時候與另外一個計算到

注意細節,考慮極值所在位置(原點、邊、新點)

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;

int a[500001][3];
int ls[200001];
int A[500001][3];
int Ls[200001];
int dfn[200001];
int low[200001];
int d[200001];
int D[200001];
int mx[200001];
int mn[200001];
bool bz[200001];
int num[200001];
int f[200001][3]; //mn,mx,ans
int n,m,Q,i,j,k,l,len,Len,tot,h,t;

void New(int x,int y,int z)
{
    ++len;
    a[len][0]=y;
    a[len][1]=ls[x];
    ls[x]=len;
    a[len][2]=z;
}
void NEW(int x,int y,int z)
{
    ++Len;
    A[Len][0]=y;
    A[Len][1]=Ls[x];
    Ls[x]=Len;
    A[Len][2]=z;
    
    ++D[y];
}

void dfs(int t)
{
    int i;
    
    ++j;
    dfn[t]=j;
    low[t]=j;
    bz[t]=1;
    d[++l]=t;
    
    for (i=ls[t]; i; i=a[i][1])
    {
        if (!dfn[a[i][0]])
        {
            dfs(a[i][0]);
            low[t]=min(low[t],low[a[i][0]]);
        }
        else
        if (bz[a[i][0]])
        low[t]=min(low[t],dfn[a[i][0]]);
    }
    
    if (dfn[t]==low[t])
    {
        ++tot;
        while (d[l]!=t)
        {
            bz[d[l]]=0;
            num[d[l--]]=tot;
        }
        bz[d[l]]=0;
        num[d[l--]]=tot;
    }
}

int main()
{
//  freopen("e.in","r",stdin);
//  freopen("b.out","w",stdout);
    
    scanf("%d%d%d",&n,&m,&Q);
    fo(i,1,m)
    {
        scanf("%d%d%d",&j,&k,&l);
        New(j,k,l);
    }
    
    memset(mx,190,sizeof(mx));
    memset(mn,60,sizeof(mn));
    j=l=0;
    dfs(1);
    
    fo(j,1,n)
    if (num[j])
    {
        for (i=ls[j]; i; i=a[i][1])
        if (num[a[i][0]])
        {
            if (num[j]==num[a[i][0]])
            {
                mx[num[j]]=max(mx[num[j]],a[i][2]);
                mn[num[j]]=min(mn[num[j]],a[i][2]);
            }
            else
            NEW(num[j],num[a[i][0]],a[i][2]);
        }
    }
    
    h=t=0;
    fo(i,1,tot)
    if (!D[i])
    d[++t]=i;
    
    fo(i,1,tot)
    {
        f[i][0]=mn[i];
        f[i][1]=mx[i];
        f[i][2]=f[i][1]-f[i][0];
    }
    while (h<t)
    {
        for (i=Ls[d[++h]]; i; i=A[i][1])
        {
            f[A[i][0]][0]=min(f[A[i][0]][0],min(f[d[h]][0],A[i][2]));
            f[A[i][0]][1]=max(f[A[i][0]][1],max(f[d[h]][1],A[i][2]));
            
            f[A[i][0]][2]=max(f[A[i][0]][2],A[i][2]-f[d[h]][0]);
            f[A[i][0]][2]=max(f[A[i][0]][2],mx[A[i][0]]-f[d[h]][0]);
            f[A[i][0]][2]=max(f[A[i][0]][2],mx[A[i][0]]-A[i][2]);
            
            f[A[i][0]][2]=max(f[A[i][0]][2],f[d[h]][1]-A[i][2]);
            f[A[i][0]][2]=max(f[A[i][0]][2],f[d[h]][1]-mn[A[i][0]]);
            f[A[i][0]][2]=max(f[A[i][0]][2],A[i][2]-mn[A[i][0]]);
            
            f[A[i][0]][2]=max(f[A[i][0]][2],max(f[d[h]][2],0));
            
            --D[A[i][0]];
            if (!D[A[i][0]])
            d[++t]=A[i][0];
        }
    }
    
    for (;Q;--Q)
    {
        scanf("%d",&j);
        if (num[j] && f[num[j]][2]>=0)
        printf("%d\n",f[num[j]][2]);
        else
        printf("-1\n");
    }
}

F

Tarjan*2

顯然能夠求出相交關係而後求割點,用主席樹優化連邊

口胡一下

按橫/豎順序掃描線,考慮相交線段a和b

a在加入時由葉子連向a,b在查找時由b連向詢問區間

每一個區間向兒子連邊,詢問時新建葉節點時就向原葉節點連邊,若是是刪除一段線段就不連

一次修改or詢問的操做節點和連邊數爲log n級別

兩種順序搞完以後Tarjan求割點便可

code

沒寫

相關文章
相關標籤/搜索