2019.07.11比赛总结

38 篇文章 0 订阅
6 篇文章 0 订阅

前言

昨晚是真的没睡好,今天的比赛貌似都是睡过去的,呵呵。。。

前两天的题都太了(*^▽^*)(可能是XC想给我们放松一下),今天直接扔了个 省选- 的比赛过来,把我们全轰了(*Φ皿Φ*)

Farmer John 此生最大的敌人!!!

T1(照片

赛时:

刚看到题,jio得还挺简单的,于是一波疯狂打码,结果打着打着,发现事情并不简单(嘿嘿嘿)

于是开始思考正解,但是。。。一头栽倒*1,睡了。

正解:

DP  
f[i]表示到i之前且i必须放的最多斑点奶牛数, 
l[i]表示放了i这个点之后,最远到哪里必须要再放一个。 
r[i]表示放了i这个点之后,最近到哪里能再放一个。 
f[i]=f[j]+1(l[i]<=j<=r[i]) 
然后可以用单调队列优化一下。

CODE

(由于本人过于蒟蒻,只能借借dalao的了)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=201000;
int n,m;
int l[N],r[N];
int f[N];
int q[N],st,ed;
int main()
{
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=n+1;i++) r[i]=i-1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        l[y+1]=max(l[y+1],x);
        r[y]=min(r[y],x-1);
    }
    memset(f,0,sizeof(f));
    for(int i=2;i<=n+1;i++) l[i]=max(l[i],l[i-1]);
    for(int i=n;i>=1;i--) r[i]=min(r[i+1],r[i]);
    st=ed=1;q[1]=0;int j=1;
    for(int i=1;i<=n+1;i++)
    {
        while(j<=r[i]&&j<=n)
        {
            if(f[j]==-1)
            {
                j++;
                continue;
            }
            while(f[j]>f[q[ed]]&&st<=ed) ed--;
            q[++ed]=j;
            j++;
        }
        while(q[st]<l[i]&&st<=ed) st++;
        if(st<=ed) f[i]=f[q[st]]+(i!=n+1?1:0);
        else f[i]=-1;
    }

    printf("%d\n",f[n+1]);
}

T2(阴阳

赛时:

不得不说,真TM困,已经是对着屏幕发呆的状态,脑子一团糨糊。

码到一半,抵挡不住困意,一头栽倒*2。

正解:(部分摘自ZLT大佬的文

很显然这是一道点分治,(我怎么没看出来?~_~?) 

这题我一开始就看错了···关键是一条路径上要找到一个分割点,然后两边的路都要是两种牛数量相同的。

好了分析一下吧,我们可以首先把不同牛看成是-1和1的权值。然后要找一条路径,和一个割点,使两端平衡嘛。我们先定个根吧,当根为rx时,我们从根上发射出size_rx条路径,我们称为分路径。现在我们要干的就是配对了。这道题可以直接开个桶做,两条路径权值加起来等于0,就有可能在答案中。那么现在问题来了,怎么样的路径才是合法的呢?

我们先考虑分路径。只要分路径上有一个点的权值是和端点的一样的,那么这条分路径就是有割点的了。

我们知道,由两条分路径合起来的路径,只要有一条是有割点的,这条路就合法了,淡然两个都有割点更不用说。所以就可以统计了。还有一些特殊情况,这个就不说了。

看看时间复杂度吧。对于一个子树,处理必须经过他路径的复杂度是size_rx的,所以就可以点分治了。

复杂度:O(N log N) ——优秀

CODE(ZLT大佬的biu)

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int S=100005;
const int N=500005;
struct rec
{
    int sig,cnt;//cnt equal pos
}buc[N*2][2],tr[N];
int dis[N],first[N*2],next[N*2],b[N*2],pd[N],c[N*2],st[N],en[N],i,x,y,t,n,t1,tt,ttt,t2,col,vis[N],f[N],cnt[N],up[N],go[N];
ll ans;
int cr(int x,int y,int t)
{
    ttt++;
    b[ttt]=y;
    c[ttt]=(t==1)?1:-1;
    next[ttt]=first[x];
    first[x]=ttt;
}
int dfs(int x,int y)
{
    tt++;
    tr[tt].cnt=dis[x];
    if (pd[dis[x]]!=0)
        tr[tt].sig=1;
    else
        tr[tt].sig=0;
    if (pd[dis[x]]==0)
        pd[dis[x]]=x;
    for(int p=first[x];p;p=next[p])
    {
        if(b[p]!=y&&!vis[b[p]])
        {
            dis[b[p]]=dis[x]+c[p];
            dfs(b[p],x);
        }
    }
    if (pd[dis[x]]==x)
        pd[dis[x]]=0;
}
int thr(int x,int y)
{
    cnt[x]=1;
    for (int p=first[x];p;p=next[p])
    {
        if (b[p]!=y&&!vis[b[p]])
        {
            thr(b[p],x);
            cnt[x]+=cnt[b[p]];
        }
    }
}
int find(int x,int y)
{
    int ret=x,tmp;
    f[x]=up[x];
    for(int p=first[x];p;p=next[p])
        if (b[p]!=y&&!vis[b[p]])
            f[x]=max(cnt[b[p]],f[x]);
    for(int p=first[x];p;p=next[p])
        if (b[p]!=y&&!vis[b[p]])
        {
            up[b[p]]=up[x]+cnt[x]-cnt[b[p]];
            tmp=find(b[p],x);
            if (f[ret]>f[tmp]) ret=tmp;
        }
    return ret;
}
ll getans()
{
    ll ret=0;
    for(int i=1;i<=t2;i++)
        for(int j=st[i];j<=en[i];j++)
        {
            if (buc[tr[j].cnt+S][tr[j].sig].sig!=col)
            {
                buc[tr[j].cnt+S][tr[j].sig].sig=col;
                buc[tr[j].cnt+S][tr[j].sig].cnt=1;
            }
            else
                buc[tr[j].cnt+S][tr[j].sig].cnt++;
        }
    for(int i=1;i<=t2;i++)
    {
        for(int j=st[i];j<=en[i];j++)   buc[tr[j].cnt+S][tr[j].sig].cnt--;
        for(int j=st[i];j<=en[i];j++)
        {
            if (tr[j].cnt==0&&tr[j].sig==1) ret++;
            if (buc[-tr[j].cnt+S][1].sig==col)
                ret+=buc[-tr[j].cnt+S][1].cnt;
            if ((tr[j].sig==1||tr[j].cnt==0)&&(buc[-tr[j].cnt+S][0].sig==col))
                ret+=buc[-tr[j].cnt+S][0].cnt;
        }
    }
    return ret;
}

int solve(int x,int y)
{
    //printf("%d\n",&x);
    vis[x]=1;
    tt=0;
    t2=0;
    for(int p=first[x];p;p=next[p])
    {
        if (vis[b[p]]==0)
        {
            //pd[0]=x;
            t2++;
            st[t2]=en[t2-1]+1;
            dis[b[p]]=dis[x]+c[p];
            dfs(b[p],x);
            en[t2]=tt;
            //pd[0]=0;
        }
    }
    col++;
    ans+=getans();
    for(int p=first[x];p;p=next[p])
    {   
        if (vis[b[p]]==0)
        {
            thr(b[p],x);
            t1++;
            up[b[p]]=0;
            go[t1]=find(b[p],x);
            dis[go[t1]]=0;
            solve(go[t1],x);
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&t);
        cr(x,y,t);
        cr(y,x,t);
    }
    thr(1,0);
    t1++;
    go[1]=find(1,0);
    dis[go[1]]=0;
    solve(go[1],0);
    printf("%lld",ans);
}

T3(数字八

赛时:

这题已经困到我连码都没码,就一头栽倒*3。

正解:

我们发现数字8中间部分(上矩形的底部,下矩形的顶部)是一个划分线:

这让我们可以把问题划分为两个子问题。

上矩形部分 

设 f[i,j,k] 为左边界为 i ,右边界为 j ,下边界为 k ,保证染色部分均为完美无瑕,的最大高度。

                          

同时,我们可以计算出s[i,j,k]为此时的最大面积:

                            s[i,j,k] = (j - i - 1) * (f[i,j,k] - 2)    if f[i,j,k] > 2

下矩形部分

与上矩形部分相似的,我们也可以定义一个下包围的结构,通过DP求出各个位置的值。

合并上下矩形

因为上矩形严格被下矩形包含,所以我们枚举下矩形的上边接,并通过辅助数组S[i,j,k] = max{s[x,y,k]} (i <= x <= y <= j),方便地求出下矩形地上边界确定时上矩形的下边界的最优值。

复杂度

时间复杂度O(N^3),空间复杂度O(N^3)

CODE(jie个也不是我的 =) )

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define N 301
using namespace std;
int n,bz,bz1;
long long ans;
char c;
int f[N][N][N],g[2][N][N];
bool map[N][N];
int a[N][N],b[N];
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			scanf(" %c",&c),map[i][j]=(c=='.'),a[i][j]=a[i][j-1]+(map[i][j]^1);
	for (int i=1;i<n;i++)
		if (map[1][i])
		for (int j=i+1;j<=n&&map[1][j];j++)
			f[1][i][j]=1;
	for (int i=2;i<=n;i++)
		for (int j=1;j<n;j++)
			for (int k=j+1;k<=n;k++)
				if (!f[i-1][j][k]){
					if (!(a[i][k]-a[i][j-1]))f[i][j][k]=i;
				}else
					if (map[i][j]&&map[i][k])
						f[i][j][k]=f[i-1][j][k];
	bz=0,bz1=1;
	for (int i=1;i<=n;i++)
		if (map[n][i])
			for (int j=i+1;j<=n&&map[n][j];j++)
				g[bz][i][j]=n;
	for (int i=n-1;i;i--,bz^=1,bz1^=1){
		for (int j=1;j<=n;j++)
			b[j]=0;
		for (int j=n-1;j;j--){
			for (int k=j+1;k<=n;k++)
				if (!(a[i][j]-a[i][k-1]))
					b[k]=max(b[k],(i-f[i][j][k]-1)*(k-j-1));
			for (int k=j+1;k<=n;k++)
				b[k]=max(b[k-1],b[k]);
			for (int k=j+1;k<=n;k++)
				if (!g[bz][j][k]){
					if (!(a[i][k]-a[i][j-1]))g[bz1][j][k]=i;
				}else{
					if (map[i][j]&&map[i][k]){
						g[bz1][j][k]=g[bz][j][k];
						if (!(a[i][k]-a[i][j-1]))
						ans=max(ans,(long long)(g[bz1][j][k]-i-1)*(k-j-1)*b[k]);
					}
					g[bz][j][k]=0;
				}
		}
	}
	!ans?printf("-1"):printf("%lld",ans);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值