POI2018

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/82865784

Plan metra:

找出 1 1 - n n 的路径后随便构造就行了,一开始我想找 d 1 + d n d_1+d_n 相等的点作为路径上的点,其实找 d 1 + d n d_1+d_n 最小的才是正确的,然后还要特判 1 1 n n 直接相连的情况,这时可以用所有点 d 1 d n |d_1-d_n| 相同来判断,但是当 d 1 d n |d_1-d_n| 都为 0 0 时, 1 1 n n 不是直接相连的,要特判。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=500010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,mn=inf,lb=0,len=0;
struct Node{int d1,dn,x,id;}a[Maxn],b[Maxn];
bool cmp(Node a,Node b){return a.d1<b.d1;}
struct Edge{int x,y,d;}e[Maxn];
int mark[1000010];
int main()
{
    n=read();bool flag=true;
    if(n==2)return printf("TAK\n1 2 1"),0;
    for(int i=2;i<n;i++)a[i].d1=read(),a[i].id=i;
    for(int i=2;i<n;i++)
    {
        a[i].dn=read();
        a[i].x=a[i].d1-a[i].dn;
        mn=min(mn,a[i].d1+a[i].dn);
        if(i>2&&abs(a[i].x)!=abs(a[i-1].x))flag=false;
    }
    if(flag)
    {
        int dis=abs(a[2].x);
        if(!dis)
        {
            if(n==3)
            {
                puts("TAK");
                printf("%d %d %d\n",1,2,a[2].d1);
                printf("%d %d %d\n",3,2,a[2].d1);
                return 0;
            }
            sort(a+2,a+n,cmp);
            if(a[2].d1==a[3].d1)return puts("NIE"),0;
            puts("TAK");
            printf("%d %d %d\n",1,a[2].id,a[2].d1);
            printf("%d %d %d\n",n,a[2].id,a[2].d1);
            for(int i=3;i<n;i++)printf("%d %d %d\n",a[2].id,a[i].id,a[i].d1-a[2].d1);
            return 0;
        }
        puts("TAK");
        printf("%d %d %d\n",1,n,dis);
        for(int i=2;i<n;i++)
        {
            if(a[i].x>0)printf("%d %d %d\n",i,n,a[i].d1-dis);
            else printf("%d %d %d\n",i,1,a[i].dn-dis);
        }
        return 0;
    }
    else
    {
        memset(mark,-1,sizeof(mark));
        int dis=mn;
        for(int i=2;i<n;i++)
        {
            if(a[i].x==dis)e[++len].x=i,e[len].y=n,e[len].d=a[i].d1-dis;
            else if(a[i].x==-dis)e[++len].x=i,e[len].y=1,e[len].d=a[i].dn-dis;
            else b[++lb]=a[i],b[lb].id=i;
        }
        sort(b+1,b+1+lb,cmp);
        int last=1,lastd=0;
        for(int i=1;i<=lb;i++)
        {
            if(b[i].d1+b[i].dn==dis)
            {
                e[++len].x=last,e[len].y=b[i].id,e[len].d=b[i].d1-lastd;
                if(e[len].d<=0)return puts("NIE"),0;
                last=b[i].id,lastd=b[i].d1;mark[b[i].d1]=b[i].id;
            }
            else
            {
                int d=b[i].d1+b[i].dn-dis;
                if(d&1)return puts("NIE"),0;
                d>>=1;
                if(b[i].d1<=d||mark[b[i].d1-d]==-1)return puts("NIE"),0;
                e[++len].x=mark[b[i].d1-d],e[len].y=b[i].id,e[len].d=d;
            }
        }
        e[++len].x=last,e[len].y=n,e[len].d=dis-lastd;
        if(len!=n-1||e[len].d<=0)return puts("NIE"),0;
        puts("TAK");
        for(int i=1;i<=len;i++)printf("%d %d %d\n",e[i].x,e[i].y,e[i].d);
    }
}

Powódź:

这个题的话抓住如果某个格子的水位高于墙,那么它周围的一些格子的水位必须跟它一样这个性质做,按照墙的高度排序,每次把两个块合并,维护合法的答案、高度即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=500010;
const int inf=2147483647;
const int mod=1000000007;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,H,cnt=0;
int P(int x,int y){return m*(x-1)+y;}
struct Node
{
    int v,type,x,y;
}a[Maxn<<1];
bool cmp(Node a,Node b){return a.v<b.v;}
int Pow(int x,int y)
{
    if(!y)return 1;
    if(y==1)return x;
    int t=Pow(x,y>>1),re=(LL)t*t%mod;
    if(y&1)re=(LL)re*x%mod;
    return re;
}
int fa[Maxn],mx[Maxn],ans[Maxn];
int findfa(int x){return(fa[x]==x?x:fa[x]=findfa(fa[x]));}
void merge(int x,int y,int h)
{
    int fx=findfa(x),fy=findfa(y);
    if(fx!=fy)
    {
        ans[fy]=(LL)(ans[fy]+h-mx[fy])*(ans[fx]+h-mx[fx])%mod;
        mx[fy]=h;
        fa[fx]=fy;
    }
}
int main()
{
    n=read(),m=read(),H=read();
    for(int i=1;i<=n;i++)
    for(int j=1;j<m;j++)
    a[++cnt].v=read(),a[cnt].x=i,a[cnt].y=j,a[cnt].type=0;
    for(int i=1;i<n;i++)
    for(int j=1;j<=m;j++)
    a[++cnt].v=read(),a[cnt].x=i,a[cnt].y=j,a[cnt].type=1;
    sort(a+1,a+1+cnt,cmp);
    for(int i=1;i<=n*m;i++)fa[i]=i,mx[i]=-1,ans[i]=0;
    for(int i=1;i<=cnt;i++)
    if(a[i].type==0)merge(P(a[i].x,a[i].y),P(a[i].x,a[i].y+1),a[i].v);
    else merge(P(a[i].x,a[i].y),P(a[i].x+1,a[i].y),a[i].v);
    printf("%d",(ans[findfa(1)]+H-mx[findfa(1)])%mod);
}

Prawnicy:

这个题做法非常显然啊,考虑答案一定是以某段开头的,那么就枚举是第 i i 段。那么也就是要找 k k 段满足 l &lt; = l i l&lt;=l_i ,然后令这 k k 个中的 r r 最小值尽量大,这个可以用数据结构实现,也可以用一个 s i z e size k k 的堆来实现。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=1000010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,k,L,R;
struct Line{int l,r,id;}a[Maxn];
bool cmp(Line a,Line b)
{
    if(a.l!=b.l)return a.l<b.l;
    return a.r>b.r;
}
priority_queue<int>q;int sz=0;
int main()
{
    n=read(),k=read();
    for(int i=1;i<=n;i++)a[i].l=read(),a[i].r=read(),a[i].id=i;
    sort(a+1,a+1+n,cmp);
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int t;
        if(sz<k)q.push(-a[i].r),sz++;
        else
        {
            t=-q.top();
            if(t<a[i].r)q.pop(),q.push(-a[i].r);
        }
        if(sz==k)
        {
            t=-q.top();
            int tmp=t-a[i].l;
            if(tmp>ans)ans=tmp,L=a[i].l,R=t;
        }
    }
    printf("%d\n",ans);int K=0;
    for(int i=1;i<=n;i++)
    if(a[i].l<=L&&a[i].r>=R)
    {
        K++;
        printf("%d ",a[i].id);
        if(K==k)break;
    }
}

Różnorodność:

这个题不会啊。考虑暴力,每次移动一个 k × k k\times k 的正方形,然后删除 k k 个位置的贡献,再加入 k k 个位置的贡献,这样是 O ( n m k ) O(nmk) 的。考虑每个位置,被反复地删除和插入,非常耗时。考虑怎么使每个格子的贡献只被考虑一次,我们可以每次计算一行( m k + 1 m-k+1 个)正方形的答案,那么每次就是删去一行数的贡献和加入一行数的贡献,对于每个数,找到它的前驱和后继(以它所在的列为关键字),然后就可以知道它影响的是哪一段区间的答案,差分就好了,找前驱后继用线段树实现,复杂度 O ( n m l o g m ) O(nmlogm) ,但是没有过。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=3010;
const int Maxv=100010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,k,a[Maxn][Maxn],cnt[Maxv],d[Maxn];
int root[Maxv],lc[Maxv<<8],rc[Maxv<<8],c[Maxv<<8],tot=0;
void up(int x){c[x]=c[lc[x]]+c[rc[x]];}
void add(int &x,int l,int r,int p,int v)
{
    if(!x)x=++tot;
    if(l==r){c[x]+=v;return;}
    int mid=l+r>>1;
    if(p<=mid)add(lc[x],l,mid,p,v);
    else add(rc[x],mid+1,r,p,v);
    up(x);
}
int q1(int x,int l,int r,int p)
{
    if(!x||!c[x])return -1;
    if(l==r)return l;
    int mid=l+r>>1;
    if(p<=mid)return q1(lc[x],l,mid,p);
    int t=q1(rc[x],mid+1,r,p);
    if(t!=-1)return t;
    return q1(lc[x],l,mid,p);
}
int q2(int x,int l,int r,int p)
{
    if(!x||!c[x])return -1;
    if(l==r)return l;
    int mid=l+r>>1;
    if(p>mid)return q2(rc[x],mid+1,r,p);
    int t=q2(lc[x],l,mid,p);
    if(t!=-1)return t;
    return q2(rc[x],mid+1,r,p);
}
void cmax(int &x,int y){x=max(x,y);}
void cmin(int &x,int y){x=min(x,y);}
int ans1=0;LL ans2=0;
int Add(int l,int r,int o)
{
    d[l]+=o;
    if(r+1<=m-k+1)d[r+1]-=o;
}
int main()
{
    n=read(),m=read(),k=read();
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    a[i][j]=read();
    for(int i=1;i<k;i++)
    for(int j=1;j<=k;j++)
    {
        if(!cnt[a[i][j]])d[1]++;
        cnt[a[i][j]]++;
    }
    for(int j=k+1;j<=m;j++)
    for(int i=1;i<k;i++)
    {
        cnt[a[i][j-k]]--;
        if(!cnt[a[i][j-k]])d[j-k+1]--;
        if(!cnt[a[i][j]])d[j-k+1]++;
        cnt[a[i][j]]++;
    }
    for(int i=1;i<k;i++)
    for(int j=1;j<=m;j++)
    add(root[a[i][j]],1,m,j,1);
    for(int i=k;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int L=q1(root[a[i][j]],1,m,j),R=q2(root[a[i][j]],1,m,j);
            int ll=max(1,j-k+1),rr=min(m-k+1,j);
            if(L!=-1)cmax(ll,L+1);
            if(R!=-1)cmin(rr,R-k);
            if(ll<=rr)Add(ll,rr,1);
            add(root[a[i][j]],1,m,j,1);
        }
        int t=0;
        for(int j=1;j<=m-k+1;j++)
        {
            t+=d[j];
            cmax(ans1,t);ans2+=t;
        }
        for(int j=1;j<=m;j++)
        {
            add(root[a[i-k+1][j]],1,m,j,-1);
            int L=q1(root[a[i-k+1][j]],1,m,j),R=q2(root[a[i-k+1][j]],1,m,j);
            int ll=max(1,j-k+1),rr=min(m-k+1,j);
            if(L!=-1)cmax(ll,L+1);
            if(R!=-1)cmin(rr,R-k);
            if(ll<=rr)Add(ll,rr,-1); 
        }
    }
    printf("%d %lld",ans1,ans2);
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/82865784
poi
今日推荐