Gym 101194C / UVALive 7899 - Mr. Panda and Strips - [set][2016 EC-Final Problem C]

题目链接:

http://codeforces.com/gym/101194/attachments

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5921

题意:

一个长度为 $N$ 的序列,要求选出两段不重叠的区间,要求两个区间包含的元素均互不相同,求两段区间的长度和最大为多少。

题解:

(主要参考https://blog.csdn.net/a1214034447/article/details/78768645

首先用 $back[i]$ 和 $front[i]$ 分别表示 $i$ 这个位置的数从i往右看第一次出现的位置,和往左看第一次出现的位置。

接着,我们从位置 $n$ 开始倒退回去枚举右区间的左端点 $r$,用 $rlen = back[r] - r$ 表示当前位置往右延伸的最长长度,并且用一个set保存目前右区间 $[r,back[r])$ 里所有的数。

然后,对于每个 $r$,均枚举左区间的右端点 $l(l<r)$,用 $llen = l - front[l]$ 表示目前该位置往左延伸的最长长度。

同时,再用一个set维护:找到左区间里,所有在右区间出现过的数,存储这些数的下标。

接下来依次枚举这些数,考虑这些数若不让其在右区间取到(当然,还有一种情况是都在右区间取),那么可以算出此时相应左区间最长能有多长。将左右区间长度求和,维护最大值。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
const int maxc=1e5+10;

int n,m;
int num[maxn],pos[maxc];
int bak[maxn],frt[maxn];
set<int> st,se;

inline int maxllen(int p,int l,int llen)
{
    auto it=se.end();
    while((it--)!=se.begin())
        if(pos[num[*it]]<p) return l-(*it);
    return llen;
}

int main()
{
    int T;
    cin>>T;
    for(int kase=1;kase<=T;kase++)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&num[i]);

        memset(pos,0,sizeof(pos));
        for(int i=1;i<=n;i++)
        {
            frt[i]=pos[num[i]];
            pos[num[i]]=i;
        }
        memset(pos,0x3f3f3f3f,sizeof(pos));
        for(int i=n;i>=1;i--)
        {
            bak[i]=pos[num[i]];
            pos[num[i]]=i;
        }

        int ans=1;
        st.clear();
        for(int i=n,rlen=1;i>=1;i--,rlen++)
        {
            while(bak[i] <= i+rlen-1)
            {
                st.erase(num[i+rlen-1]);
                rlen--;
            }
            st.insert(num[i]), pos[num[i]]=i;
            se.clear();
            for(int j=1,llen=1;j<i;j++,llen++)
            {
                while(frt[j] >= j-llen+1)
                {
                    se.erase(j-llen+1);
                    llen--;
                }
                if(st.count(num[j])) se.insert(j);
                if(!se.size()) ans=max(ans,llen+rlen);
                else ans=max(ans,j-*(--se.end())+rlen);
                for(auto it=se.begin();it!=se.end();it++)
                    ans=max(ans,maxllen(pos[num[*it]],j,llen)+pos[num[*it]]-i);
            }
        }
        printf("Case #%d: %d\n",kase,ans);
    }
}

原文地址:https://www.cnblogs.com/dilthey/p/9902050.html

时间: 2024-08-05 08:32:42

Gym 101194C / UVALive 7899 - Mr. Panda and Strips - [set][2016 EC-Final Problem C]的相关文章

hdu6007 Mr. Panda and Crystal 最短路+完全背包

/** 题目:hdu6007 Mr. Panda and Crystal 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6007 题意:魔法师有m能量,有n种宝石,有些宝石给定了用魔法变出它需要的能量,以及该宝石可以卖出的价钱. 有些宝石没有给出,给出k个方程,表示某些宝石可以通过另外一些宝石合成. 求魔法师最多可以卖出多少钱. 思路: 处理方程,最短路求出所有的宝石用能量变出的最小能量值. 然后完全背包. */ #include<iostream>

Gym 101194F Mr. Panda and Fantastic Beasts

#include<bits/stdc++.h> using namespace std; #define ms(arr,a) memset(arr,a,sizeof arr) #define debug(x) cout<<"< "#x" = "<<x<<" >"<<endl const int maxn=4e5; const int INF=0x3f3f3f3f; char

Gym 100299C &amp;&amp; UVaLive 6582 Magical GCD (暴力+数论)

题意:给出一个长度在 100 000 以内的正整数序列,大小不超过 10^ 12.求一个连续子序列,使得在所有的连续子序列中, 它们的GCD值乘以它们的长度最大. 析:暴力枚举右端点,然后在枚举左端点时,我们对gcd相同的只保留一个,那就是左端点最小的那个,只有这样才能保证是最大,然后删掉没用的. UVaLive上的数据有问题,比赛时怎么也交不过,后来去别的oj交就过了. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000&qu

[acm/icpc2016ChinaFinal][CodeforcesGym101194] Mr. Panda and Fantastic Beasts

地址:http://codeforces.com/gym/101194 题目:略 思路: 这题做法挺多的,可以sam也可以后缀数组,我用sam做的. 1.我自己yy的思路(瞎bb的) 把第一个串建立sam,然后让其他串在上面跑. 每走到一个位置p,当前长度为num,就代表这个endpos集合里的长度小于等于num的字符串是被包含了,同时parent树的所有祖先节点也要标记为被包含. 这一步的具体做法是:用一个mi数组表示到达该节p点时的长度大于mi[p]时,才算未被包含(到达长度指的是从root

Gym 101194L / UVALive 7908 - World Cup - [三进制状压暴力枚举][2016 EC-Final Problem L]

题目链接: http://codeforces.com/gym/101194/attachments https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5930 题意: 现有四支队伍两两打比赛,总共就是打六场比赛,每场比赛赢的队伍可得 $3$ 分,输的队伍得 $0$ 分,平局则两个队各得 $1$ 分. 现在给出四个队伍最终的积分

Mr. Panda and Crystal HDU - 6007 最短路+完全背包

题目:题目链接 思路:不难看出,合成每个宝石需要消耗一定的魔力值,每个宝石有一定的收益,所以只要我们知道每个宝石合成的最小花费,该题就可以转化为一个背包容量为初始魔力值的完全背包问题,每个宝石的最小花费可以用dijkstra跑一遍最短路算出,路径长度用合成花费表示. AC代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #in

【费用流】 ICPC 2016 China Final J. Mr.Panda and TubeMaster

表示“必须选”的模型 题目大意 题目分析 一个格子有四种方式看上去很难处理.将横竖两个方向分开考虑,会发现:因为收益只与相邻格子是否连通有关,所以可以将一个格子拆成表示横竖两个方向的,互相独立的点. 上图的格子里四个方向红边表示的就是一个格子的可能方向:拆点后所连蓝边的容量为1,费用即为连通两个格子的收益. 但是这样建图不能够表示某些格子必须要选. 考虑一个格子如果被选择了会发生什么:因为每个格子都处在环上,那么被选择的网格一定可以通过其他节点走到汇点.这意味着一个格子拆成的两个节点之间的边就可

2016-2017 ACM-ICPC CHINA-Final

A Gym 101194A Number Theory Problem B Gym 101194B Hemi Palindrome C Gym 101194C Mr. Panda and Strips D Gym 101194D Ice Cream Tower E Gym 101194E Bet F Gym 101194F Mr. Panda and Fantastic Beasts G Gym 101194G Pandaria H Gym 101194H Great Cells I Gym 1

2016-2017 ACM-ICPC CHINA-Final Solution

Problem A. Number Theory Problem Solved. 水. 1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 1e5 + 10; 6 7 typedef long long ll; 8 9 int n; 10 ll arr[maxn], ans[maxn]; 11 12 void Init() 13 { 14 arr[0] = 1; 15 for(int i = 1; i