20181020模拟赛 (1.贪心+2.树dfs序+3.DP单调队列优化+4.模拟)

Problem 1 嚎叫响彻在贪婪的厂房
程序名:factory.c/cpp/pas
时间限制:1s
空间限制:256M
RX:把机器人Hobo,带往改造工厂。
来到了改造工厂,Hobo 感到阵阵迷茫,不知道自己将会何去何从。
“当我从这里离开的时候,我还会是Eddie 的朋友吗?”
RX:珍娜女王,可以开始了。
铁斯塔:珍娜女王,等到把Hobo 改造完了,你父亲的遗愿就能实现了。
【问题描述】
机器人的哀嚎传遍了整座工厂,于是,Hobo 决定带着他们一起逃离这里。工
厂的传送带上依次排列着N 个机器人,其中,第i 个机器人的质量为Ai。Hobo
经过仔细观察,发现:
1.来自同一个家族的机器人,在这N 个机器人中一定是连续的一段。
2.如果从第i 个机器人到第j 个机器人都来自同一个家族,那么Ai 到Aj 从
小到大排序后一定是公差大于1 的等差数列的子序列。
Hobo 发现,不同家族的个数越少,机器人就会越团结,成功逃离工厂的概率
就会越高。Hobo 想知道,这N 个机器人最少来自几个不同的家族呢?
【输入格式】
第一行一个正整数N。
接下来一行N 个正整数,第i 个正整数为Ai。
【输出格式】
一行一个正整数,表示答案。
【样例输入1】
7
1 5 11 2 6 4 7
【样例输出1】
3
【样例说明1】
1 5 11 是等差数列{1,3,5,7,9,11}的子序列,
2 4 6 是等差数列{2,4,6,8}的子序列,
7 是等差数列{7,9,11}的子序列。
【样例输入2】
8
4 2 6 8 5 3 1 7
【样例输出2】
2
【样例说明2】
2 4 6 8 是等差数列{2,4,6,8}的子序列,
1 3 5 7 是等差数列{1,3,5,7}的子序列。
【数据范围】
20%的数据满足,N≤10。
40%的数据满足,N≤100。
60%的数据满足,N≤1000,1≤Ai≤10^6。
另有20%的数据满足,Ai 互不相同。
100%的数据满足,N≤100000,1≤Ai≤10^9。
Problem 2 主仆见证了Hobo 的离别
程序名:friendship.c/cpp/pas
时间限制:1s
空间限制:256M
最终,Hobo 还是没能逃出改造工厂。传送带滚滚向前,把Hobo 送入无尽的黑暗。
与此同时,Millie 家中。
Eddie: Hobo 在哪?
Millie: 他被带去“机器人改造计划”了。
Eddie: 机器人改造计划是什么?Hobo 会变成什么样?
Millie: ……
五天后。
Eddie: Hobo,你可终于回来了,我……
Hobo: 我是A-01 型工作机器人,请主人吩咐工作。
【问题描述】
Eddie 终于意识到了改造计划的本质,恨自己没能阻止这一切的发生。在
Millie 和Simon 的帮助下,他拆开了Hobo 的电路板,决定帮助Hobo 找回记忆。
一开始,Hobo 的中央处理器有N 个元件(编号为1 到N),每一个元件都存
储了一段时间的记忆。也就是说,每个元件都可以看做一个集合。为了唤醒Hobo
的回忆,他会时不时地在当前的所有元件中选择K 个进行一次融合,组成一个新
的元件,新元件的编号等于融合之前元件的总个数加一。当然,参与融合的K
个元件融合之后依然存在,并且每个元件至多参与一次融合。
由于元件的容量有限,Eddie 没有能力唤醒Hobo 全部的回忆,所以他会用下
列两种方式来融合元件:
1.集合的交:一段记忆存储在新的元件中,当且仅当这段记忆在参与融合的
K 个元件中都有储存。
2.集合的并:一段记忆存储在新的元件中,当且仅当这段记忆在参与融合的
至少一个元件中有储存。
在融合元件的过程中,Eddie 迫切地想知道:凡是存储在X 号元件中的记忆,
是否一定也存储在Y 号元件中?
【输入格式】
第一行两个整数N,M。
接下来M 行,表示有M 种操作:
0 op K A1 A2 … AK 表示将A1,A2,…,AK 合成为一个新的元件,op=0 表示
融合的方式是集合的交,op=1 表示融合的方式是集合的并。
1 X Y 表示一组询问,X,Y 的意义参见问题描述。保证X 和Y 都不会超过当
前元件的总个数。
【输出格式】
对于每个询问,输出一行一个0 或1,1 表示一定存储在Y 号元件中,否则
用0 表示。
【样例输入】
3 5
0 0 2 1 2
1 1 4
0 1 2 3 4
1 4 5
1 4 2
【样例输出】
0
1
1
【样例说明】
如果存在这样一段记忆,它存储在1 号元件中,但是它不存储在2 号元件中,那么它就
不会存储在4 号元件中,所以第一个询问的答案为0。
而对于任意一段存储在4 号元件中的记忆,它必然同时存储在2 号和5 号元件中,所以
第二、第三个询问的答案均为1。
【数据范围】
测试点N= M= ΣK≤ 备注
1 2000 2000 2000 K=1
2 2000 2000 2000 K=1
3 2000 2000 2000 前1999 个操作均不含询问操作
4 2000 2000 2000 前1000 个操作不含询问操作,
后1000 个操作都是询问操作。
5 5000 5000 10000 只存在”集合的交”和询问操作
6 5000 5000 10000 只存在”集合的交”和询问操作
7 5000 5000 10000 只存在”集合的并”和询问操作
8 100000 100000 200000 所有的询问操作中X≤Y
9 100000 100000 300000 所有的询问操作中X≥Y
10 250000 250000 500000 无
Problem 3 征途堆积出友情的永恒
程序名:empire.c/cpp/pas
时间限制:1s
空间限制:256M
Millie:Hobo,机器人和人类本来相处得这么好,为什么珍娜女王要下令改造机
器人啊?
Hobo:她的父亲,舍身救起了一个正在施工的机器人,自己却被压在了起重机下,
再也没能醒来。父亲死后,铁斯塔蛊惑她,说:“机器人是没有灵魂的,应该把
他们改造成人类的奴仆,这也是为了你的父亲。”
Millie:怎么会这样……
【问题描述】
为了说服珍娜女王停止改造计划,Eddie 和Hobo 踏上了去往王宫的征程。
Sunshine Empire 依次排列着(N+1)座城市,0 号城市是出发地,N 号城市是王宫。
火车是Sunshine Empire 的主要交通工具。Eddie 和Hobo 可以在当前的城市
上车,并且在之后的某一座城市下车。从第(i-1)座城市乘坐到第i 座城市需要
花费Ai 的费用。同时,在第i 座城市上车需要缴纳Bi 的税款。其中,税款属于
额外支出,不属于乘坐这段火车的费用。
珍娜女王为了促进Sunshine Empire 的繁荣发展,下令:如果连续地乘坐一
段火车的费用大于这次上车前所需缴纳的税款,则这次上车前的税款可以被免
除,否则免去乘坐这段火车的费用。
然而,为了保证火车的正常运行,每一次乘坐都不能连续经过超过K 座城市
(不包括上车时所在的城市),并且,Eddie 和Hobo 的移动范围都不能超出
Sunshine Empire。Eddie 想知道,到达王宫的最小总花费是多少?
【输入格式】
第一行两个整数N,K,意义同题目描述所示。
接下来一行N 个整数,第i 个整数为Ai。
接下来一行N 个整数,第i 个整数为B(i-1)。
【输出格式】
一行一个整数,表示总花费的最小值。
【样例输入1】
4 2
4 3 2 1
1 2 4 4
【样例输出1】
11
【样例说明1】
第一段火车经过0,1,2 号城市,由于4 + 3 > 1,所以税款免除,花费为7。
第二段火车经过2,3,4 号城市,由于2 + 1 < 4,所以乘坐火车的费用免除,花费为4。
总费用为7 + 4 = 11。
【样例输入2】
4 2
4 3 2 1
1 2 10 3
【样例输出2】
12
【样例说明2】
第一段火车经过0,1 号城市,花费为4。
第二段火车经过1,2,3 号城市,花费为5。
第三段火车经过3,4 号城市,花费为3。
总费用为4 + 5 + 3 = 12。
【数据范围】
20%的数据满足,N≤30,K≤5。
50%的数据满足,N≤10000,K≤1000。
另有10%的数据满足,Bi = 1。
另有10%的数据满足,Ai = B(i-1)。
另有10%的数据满足,N = K。
100%的数据满足,N≤500000,K≤100000,1≤Ai,Bi≤10^6。

T1

贪心,最长连续的公共gcd大于1,且两两互不相同的最大划分。
为什么贪心是对的呢,这么想,对于某个元素 a [ i ] a[i] 来说,它既可以作为前面往后的划分,也可以单独往后划分,但无论前后没有它,也都可以存在,所以可以贪心。
两两互不相同则用map维护(或者你闲的话打个离散化也可以)

# include <bits/stdc++.h>
# define Rint register int 
# define int long long
using namespace std;
const int MAXN=1e5+10;
int n,a[MAXN];
map<int,int>mp;
inline int gcd(Rint a,Rint b){ return !b?a:gcd(b,a%b); }
signed main()
{
//  freopen("factory.in","r",stdin);
//  freopen("factory.out","w",stdout);
    int n,ans=0; scanf("%lld",&n);
    for (Rint i=1;i<=n;i++) scanf("%lld",&a[i]);
    int l=1;
    while (l<=n) {
        int r=l+1; 
        int g=abs(a[r]-a[l]);
        mp[a[l]]=1;
        while (r<=n&&(gcd(abs(a[r]-a[l]),g)!=1)&&(mp.count(a[r])==0)) mp[a[r]]=1,r++,g=gcd(g,abs(a[r]-a[l]));
        r--; l=r+1; ans++; mp.clear();
    }
    printf("%lld\n",ans);
    return 0;
}

T2

考场可能有点不在状态没发现是颗树,
于是强行构图暴力,这么构图
譬如一些元素相交所产生新的点,那么把新的点往这些老点连单向边,(新点包含信息肯定旧点都有)
相并反之,
k=1时建立双向边
求x—>y是否存在路径即可,
不知道是树,所以直接dfs,知道以后则,
lca,这个就不说了
dfs序:对于x,y来说,若x和y的DFS区间不存在包含关系,则答案为0。反之为1。

#include<bits/stdc++.h>
using namespace std;
#define N 2100000

int dfn[N], last[N], f[N][2], low[N], du[N], a[N];
int dep[N], n, m, tot, x, y, t;
int op, k, i, j, u, v, cnt, tim;

struct edge { int v, next; } e[N]; 
struct query { int x, y; } Q[N];

void add(int u, int v){
  e[++cnt] = (edge){ v, last[u] };
  last[u] = cnt;
}

void dfs(int x){
  dfn[x] = ++tim;
  for (int i = last[x]; i; i=e[i].next){
    if (a[x] == 0 || du[x] == 1)
      f[e[i].v][0] = f[x][0] + 1;
    else
      f[e[i].v][0] = 0;
    if (a[x] == 1 || du[x] == 1)
      f[e[i].v][1] = f[x][1] + 1;
    else
      f[e[i].v][1] = 0;
    dep[e[i].v] = dep[x]+1;
    dfs(e[i].v);
  }
  low[x] = tim;
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  cin >> n >> m;
  tot = 0;
  for (int i = 1; i <= m; ++i) {
    cin >> t;
    if(t == 0) {
      cin >> op >> k;
      ++n;
      for (int j = 1; j <= k; ++j) {
        cin >> u;
        add(n, u);
      }
      a[n] = op;
      du[n] = k;
    } else {
      cin >> x >> y;
      Q[++tot] = (query){ x, y };
    }
  }
  for (int i = n; i >= 1; --i) if (!dfn[i]) dfs(i);
  for (int i = 1; i <= tot; ++i) {
    u = Q[i].x;
    v = Q[i].y;
    if (dfn[u] <= dfn[v] && low[v] <= low[u]) {
      if (f[v][0] >= dep[v] - dep[u]) puts("1");
      else puts("0");     
    } else if (dfn[v] <= dfn[u] && low[u] <= low[v]) {
      if (f[u][1] >= dep[u] - dep[v]) puts("1");
      else puts("0");      
    } else puts("0");
  }
  return 0;
}

T3
额,对于DP的优化我不是很会,所以直接暴力DP,然后特判了两种情况,50.
果然……
单调队列优化DP,
令sum[i]表示a[i]的前缀和。
易得状态转移方程: f [ i ] = m i n ( f [ j ] + m a x ( s u m [ i ] s u m [ j ] , b [ j + 1 ] ) ) i k j i 1 f[i] = min(f[j] + max(sum[i] - sum[j],b[j + 1])),其中 i - k ≤ j ≤ i - 1。
注意到对于任意的j, f [ j ] s u m [ j ] + s u m [ i ] f[j]-sum[j]+sum[i] i i 单调增,而 f [ j ] + b [ j + 1 ] f[j]+b[j+1] 是定值。
所以考虑使用堆来优化DP。
建两个小根堆,一个维护 f [ j ] + b [ j + 1 ] f[j]+b[j+1] ,另一个维护 f [ j ] s u m [ j ] f[j]-sum[j] ,则答案为min(第一个堆的最小值,第二个堆的最小值 + sum[i])。
如果取到的j小于i-k,则弹出堆顶继续取。
在第一个堆中取数时,如果发现它比 f [ j ] s u m [ j ] + s u m [ i ] f[j]-sum[j]+sum[i] 小,则把j加进第二个堆,弹出第一个堆的堆顶继续取,直到取到符合条件的为止。
显然,一个j至多进堆2次,出堆2次。

#include<bits/stdc++.h>
using namespace std;
const long long inf=0x7ffffffffff;
int n,k,a[500010],b[500010];
long long f[500010],sum[500010];
inline int read()
{
    int x=0;bool w=false;char c=0;
    while(c>'9'||c<'0') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return w?-x:x;
}
struct data
{
    int j;long long val;
    bool operator < (const data &x) const{return val>x.val;}
};
priority_queue<data> q1,q2;
int main()
{
    n=read(),k=read();
    sum[0]=0;
    for(int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+(long long)a[i];
    for(int i=1;i<=n;i++) b[i-1]=read();
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++)
    {
        q1.push((data){i-1,f[i-1]+b[i-1]});
        while(!q1.empty())
        {
            data nw=q1.top();
            if(i-nw.j>k) q1.pop();
            else break;
        }
        while(!q2.empty())
        {
            data nw=q2.top();
            if(i-nw.j>k) q2.pop();
            else break;
        }
        while(!q1.empty())
        {
            data nw=q1.top();
            if(nw.val>f[nw.j]+sum[i]-sum[nw.j]) break;
            q1.pop();
            q2.push((data){nw.j,f[nw.j]-sum[nw.j]});
        }
        while(!q1.empty())
        {
            data nw=q1.top();
            if(i-nw.j>k) q1.pop();
            else break;
        }
        while(!q2.empty())
        {
            data nw=q2.top();
            if(i-nw.j>k) q2.pop();
            else break;
        }
        long long tmp=inf;
        if(!q1.empty()) tmp=min(tmp,q1.top().val);
        if(!q2.empty()) tmp=min(tmp,q2.top().val+sum[i]);
        f[i]=tmp;
    }
    printf("%lld",f[n]);
    return 0;
}

T4
这题真是rz题
我心态崩了
我们学校机房1有毒,一模一样的程序,我在那里把样例和手测数据全过了,结果到了竞赛教室,样例都过不了???tmd所以没查出错
很简单,我的方法,相同长度计入一个vector,一遍扫过去,对于最极限的 b [ l e n ] [ j ] b [ l e n ] [ i ] &lt; = m b[len][j]-b[len][i]&lt;=m ,以 i i 为开头的序列长度就是 j i j-i

#include<bits/stdc++.h>
using namespace std;

char s[300010];
int a[300010],n,m;
vector<int> b[30];
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++)
      {
        scanf("%s",s);
        b[strlen(s)].push_back(i);
        //cout<<a[i]<<' ';
      }
      long long ans=0;
    for (int len=1; len<=20; len++)
      {
        int i=0,j=0;
        while (i<b[len].size())
          {
            while (b[len][j+1]-b[len][i]<=m&&j+1<b[len].size())//&&(j<b[len].size())) 
                  j++;
            ans+=j-i;
            //cout<<i<<' '<<j<<endl;
            i++;
          }
      }
    printf("%lld",ans);
    
}

好了,今天预估 100+70+50+100
实际 0+70+50+0
呕呕呕

猜你喜欢

转载自blog.csdn.net/beautiful_CXW/article/details/83214077