2.23模拟赛

浪了一发普及模拟赛

题面见此

【样例输入】

4

1 1

2 1

1 1

【样例输出】

20

【数据规模和范围】

对于 30%的数据,n≤1000。

对于另外 30%的数据,bi=1。

对于 100%的数据,n≤1 000 000,bi≤1000。

sol:单边记贡献,(x,y)边的贡献就是 Size[y]*(n-Size[y])*Dis[x][y],因为父亲都小于当前点,直接倒着跑一遍记Size就可以了,如果无序的话可以用拓扑

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll S=0;
    bool f=0;
    char ch=‘ ‘;
    while(!isdigit(ch))
    {
        f|=(ch==‘-‘); ch=getchar();
    }
    while(isdigit(ch))
    {
        S=(S<<3)+(S<<1)+(ch-‘0‘); ch=getchar();
    }
    return (f)?(-S):(S);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(‘-‘); x=-x;
    }
    if(x<10)
    {
        putchar(x+‘0‘);    return;
    }
    write(x/10); putchar(x%10+‘0‘);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) write(x),putchar(‘\n‘)
const int N=1000005;
int n,Father[N],Size[N];
int Val[N];
int main()
{
    freopen("fst.in","r",stdin);
    freopen("fst.out","w",stdout);
    int i;
    ll ans=0;
    R(n);
    for(i=1;i<=n;i++)
    {
        Size[i]=1;
    }
    for(i=2;i<=n;i++)
    {
        Father[i]=read(); Val[i]=read();
    }
    for(i=n;i>1;i--)
    {
        Size[Father[i]]+=Size[i];
    }
    for(i=2;i<=n;i++)
    {
        ans+=1ll*Val[i]*Size[i]*(n-Size[i]);
    }
    Wl(ans<<1);
    return 0;
}
/*
input
5
1 2
2 4
2 1
4 4
output
92

input
5
1 5
1 5
2 3
2 5
output
164
*/

2.贰(fstagain)

【题目描述】

给你一个长度为 n 的序列,有 m 次询问,让你求区间 gcd。

【输入格式】

输入文件为 fstagain.in。 第一行两个正整数 n 和 m。 第二行 n 个正整数 ai,保证 ai≤1 000 000 000。 接下来 m 行,每行两个正整数 l 和 r,询问序列中 l 到 r 的 gcd。

【输出格式】

输出文件为 fst.in。 输出 m 行,表示查询的结果。

【样例输入】

3 2

3 6 8

1 2

2 3

【样例输出】

3

2

【数据规模和范围】

对于 30%的数据,n,m≤1000。

对于 60%的数据,n≤1000。

对于 100%的数据,n,m≤100000。

sol:因为重复没关系,ST表可以搞,线段树应该也可以做(反正是模板),我写了分块(反正都能过)

#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll S=0;
    bool f=0;
    char ch=‘ ‘;
    while(!isdigit(ch))
    {
        f|=(ch==‘-‘); ch=getchar();
    }
    while(isdigit(ch))
    {
        S=(S<<3)+(S<<1)+(ch-‘0‘); ch=getchar();
    }
    return (f)?(-S):(S);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(‘-‘); x=-x;
    }
    if(x<10)
    {
        putchar(x+‘0‘);    return;
    }
    write(x/10); putchar(x%10+‘0‘);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) write(x),putchar(‘\n‘)
const int N=100005,B=355;
int n,m,a[N];
int Block,cnt,L[B],R[B],Gcd[B],Pos[N];
inline int gcd(int x,int y)
{
    return (!y)?(x):(gcd(y,x%y));
}
inline void Pre()
{
    int i,j;
    Block=sqrt(n);
    cnt=n/Block+(bool)(n%Block);
    for(i=1;i<=cnt;i++)
    {
        L[i]=R[i-1]+1;
        R[i]=L[i]+Block-1;
    }
    R[cnt]=n;
    for(i=1;i<=cnt;i++)
    {
        Gcd[i]=a[L[i]];
        Pos[L[i]]=i;
        for(j=L[i]+1;j<=R[i];j++)
        {
            Pos[j]=i;
            Gcd[i]=gcd(Gcd[i],a[j]);
        }
    }
    return;
}
inline int Solve(int l,int r)
{
    int i;
    int ql=Pos[l],qr=Pos[r];
    if(ql+1>=qr)
    {
        int ans=a[l];
        for(i=l+1;i<=r;i++)
        {
            ans=gcd(ans,a[i]);
        }
        return ans;
    }
    else
    {
        int ans=a[l];
        for(i=l+1;i<L[ql+1];i++)
        {
            ans=gcd(ans,a[i]);
        }
        for(i=ql+1;i<qr;i++)
        {
            ans=gcd(ans,Gcd[i]);
        }
        for(i=L[qr];i<=r;i++)
        {
            ans=gcd(ans,a[i]);
        }
        return ans;
    }
}
int main()
{
    freopen("fstagain.in","r",stdin);
    freopen("fstagain.out","w",stdout);
    int i;
    R(n); R(m);
    for(i=1;i<=n;i++)
    {
        R(a[i]);
    }
    Pre();
    for(i=1;i<=m;i++)
    {
        int l=read(),r=read();
        Wl(Solve(l,r));
    }
    return 0;
}
/*
input
3 2
3 6 8
1 2
2 3
output
3
2
*/

3.叁(fstalways)

【题目描述】

有 4 种硬币,面值分别为 c1,c2,c3,c4。某人去商店买东西,去 了 tot 次。每次带 di 枚 ci 硬币,买 s 的价值的东西。求每次有多少 种付款方法。

【输入格式】

输入文件为 fstalways.in。 第一行 5 个正整数 c1,c2,c3,c4,tot。 接下来 tot 行,每行 5 个正整数表示的 d1,d2,d3,d4,s。

【输出格式】

输出文件为 fstalways.out。 输出 tot 行,表示每次的方案数。

【样例输入】

1 2 5 10 2

3 2 3 1 10

1000 2 2 2 900

【样例输出】

4

27

【数据规模和范围】

对于 30%的数据,di≤10。

对于 50%的数据,s,tot≤1000。

对于 100%的数据,s≤1000000,tot≤100000。

sol:先跑一遍没有 di 限制的背包,然后容斥,奇加偶减(这是套路)

容斥过程:对于S,选择第 i 种硬币超限的方案数就是 dp[S-(di+1)*c[i]],因为是完全背包,剩下的S-(di+1)*c[i]可以随便选,然而就算一个 i 也不选也是会超限的

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=‘ ‘;
    while(!isdigit(ch))
    {
        f|=(ch==‘-‘); ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48); ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(‘-‘); x=-x;
    }
    if(x<10)
    {
        putchar(x+‘0‘);    return;
    }
    write(x/10);
    putchar((x%10)+‘0‘);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) write(x),putchar(‘\n‘)
int C[5],D[5];
ll dp[1000005];
int main()
{
    freopen("fstalways.in","r",stdin);
    freopen("fstalways.out","w",stdout);
    int i,j,T,Sum;
    ll ans=0;
    for(i=1;i<=4;i++)
    {
        R(C[i]);
    }
    R(T);
    dp[0]=1;
    for(i=1;i<=4;i++)
    {
        for(j=C[i];j<=1000000;j++)
        {
            dp[j]+=dp[j-C[i]];
        }
    }
    while(T--)
    {
        ans=0;
        for(i=1;i<=4;i++)
        {
            R(D[i]);
        }
        R(Sum);
        for(i=0;i<=15;i++)
        {
            int SS=Sum,Bo=1;
            for(j=1;j<=4;j++) if(i&(1<<(j-1)))
            {
                SS-=C[j]*(D[j]+1);
                Bo^=1;
            }
            if(SS<0) continue;
            if(Bo) ans+=dp[SS]; else ans-=dp[SS];
        }
        Wl(ans);
    }
    return 0;
}
/*
input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
output
4
27
*/

4.肆(fstforever)

【题目描述】

给定一棵 n 个节点的有根树,编号依次为 1 到 n,其中 1 号点为 根节点。每个点有一个权值 vi。 现在,你需要选择尽可能多的节点,满足以下的性质:对于任意 两个点 i,j,如果 i 在树上是 j 的祖先,那么 vi>vj。请计算可选的最 多的点数。

【输入格式】

输入文件为 fstforever.in。 第一行一个正整数 n。 第二行 n-1 个正整数 ai,表示 i+1 的父亲(ai≤i+1)。 第三行 n 个整数,第 i 个整数表示 vi。 【输出格式】 输出文件为 fstforever.out。 输出一个整数,为最多的点数。

【样例输入】

6

1 1 1 1 1

3 1 2 3 4 5

【样例输出】

5

【数据规模和范围】

对于 30%的数据,n≤20。

对于 50%的数据,保证 n≤3000。

对于另外 20%的数据,保证 ai=i。

对于 100%的数据,vi≤1 000 000 000,n≤200000。

sol:对于一条链,就是跑最长上升子序列,在一棵树上是 ,我们发现对于一个非叶子节点,他所有儿子都是等价的,用multiset启发式合并就可以了

其实着重解释一下一种求LIS的方法 为什么是对的

方法是这样的:维护一个multiset,即每次新加一个元素时,在维护的multiset中找到比它大的第一个元素,替换,(如果没有就直接加入这个元素)最后multiset的长度就是LIS的长度。

为什么是替换掉比他大的第一个,而不是把所有比他大的都去掉呢?

(1)如果被替换掉的是multiset里最大的元素,就表示当前点的值加入当前维护的LIS中,并在维护的LIS中删除被替换的值

(2)如果替换之后,multiset里有比当前节点的值更大的元素,就表示不将当前节点的值加入LIS,LIS的长度不变

(3)如果没有更大的元素,就直接加入当前的值,LIS长度+1。

每次新加入元素的时候其实只跟当前LIS里最大的元素有关

要么是加入新元素,最大元素变小,LIS长度不变

要么是加入新元素、最大元素变大、LIS长度+1

要么是不加入新元素、LIS不变

#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=‘ ‘;
    while(!isdigit(ch))
    {
        f|=(ch==‘-‘); ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48); ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(‘-‘); x=-x;
    }
    if(x<10)
    {
        putchar(x+‘0‘);    return;
    }
    write(x/10);
    putchar((x%10)+‘0‘);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) write(x),putchar(‘\n‘)
const int N=200005,M=400005;
int n,Val[N];
struct Tree
{
    int tot,Next[M],to[M],head[N];
    inline void add(int x,int y)
    {
        Next[++tot]=head[x];
        to[tot]=y;
        head[x]=tot;
        return;
    }
    multiset<int>S[N];
    inline void Merg(int x,int y)
    {
        multiset<int>::iterator it;
        if(S[x].size()<S[y].size()) swap(S[x],S[y]);
        for(it=S[y].begin();it!=S[y].end();it++)
        {
            S[x].insert(*it);
        }
        return;
    }
    inline void dfs(int x)
    {
        int i;
        for(i=head[x];i;i=Next[i])
        {
            dfs(to[i]);
            Merg(x,to[i]);
        }
        if(S[x].size())
        {
            multiset<int>::iterator it;
            it=S[x].lower_bound(Val[x]);
            if(it!=S[x].end())
            {
                S[x].erase(it);
            }
        }
        S[x].insert(Val[x]);
        return;
    }
    inline void Init()
    {
        tot=0;
        memset(head,0,sizeof head);
        return;
    }
}T;
int main()
{
    freopen("fstforever.in","r",stdin);
    freopen("fstforever.out","w",stdout);
    int i;
    R(n);
    T.Init();
    for(i=2;i<=n;i++)
    {
        T.add(read(),i);
    }
    for(i=1;i<=n;i++)
    {
        R(Val[i]);
    }
    T.dfs(1);
    Wl(T.S[1].size());
    return 0;
}
/*
input
6
1 1 1 1 1
3 1 2 3 4 5
output
5
*/

原文地址:https://www.cnblogs.com/gaojunonly1/p/10425363.html

时间: 2024-11-08 20:09:55

2.23模拟赛的相关文章

【BZOJ】【2741】【FOTILE模拟赛】L

可持久化Trie+分块 神题……Orz zyf & lyd 首先我们先将整个序列搞个前缀异或和,那么某一段的异或和,就变成了两个数的异或和,所以我们就将询问[某个区间中最大的区间异或和]改变成[某个区间中 max(两个数的异或和)] 要是我们能将所有[l,r]的答案都预处理出来,那么我们就可以O(1)回答了:然而我们并不能. 一个常见的折中方案:分块! 这里先假设我们实现了一个神奇的函数ask(l,r,x),可以帮我们求出[l,r]这个区间中的数,与x最大的异或值. 我们不预处理所有的左端点,我

10.30 NFLS-NOIP模拟赛 解题报告

总结:今天去了NOIP模拟赛,其实是几道USACO的经典的题目,第一题和最后一题都有思路,第二题是我一开始写了个spfa,写了一半中途发现应该是矩阵乘法,然后没做完,然后就没有然后了!第二题的暴力都没码QAQ 现在我来写解题报告了,有点饿了QAQ.. 第一题 题目 1: 架设电话线 [Jeffrey Wang, 2007] 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务,于 是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线.新的电话线架设 在已有的N(2 <=

【题解】PAT团体程序设计天梯赛 - 模拟赛

由于本人愚笨,最后一题实在无力AC,于是只有前14题的题解Orz 总的来说,这次模拟赛的题目不算难,前14题基本上一眼就有思路,但是某些题写起来确实不太容易,编码复杂度有点高~ L1-1 N个数求和 设计一个分数类,重载加法运算符,注意要约分,用欧几里得算法求个最大公约数即可. 1 #include <cstdio> 2 3 long long abs(long long x) 4 { 5 return x < 0 ? -x : x; 6 } 7 8 long long gcd(long

[铁一中OI模拟赛]2017.8.19 Day1

T1 小Z的情书 题目链接 思考: 题目主要难度在于旋转后的位置如何,在手写了样例之后不难发现规律. #include <cstdio> #include <cstring> #define up(a,b,c) for(register int c=a;c<=b;++c) #define down(a,b,c) for(register int c=a;c>=b;--c) const int Maxn=1005; int n; bool Map[Maxn][Maxn],

bzoj2741: 【FOTILE模拟赛】L

2741: [FOTILE模拟赛]L Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 2679  Solved: 766[Submit][Status][Discuss] Description FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和. 即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r. 为了体现在线操作,对于一

140725模拟赛总结

A:hdu4847    字符串匹配第一想法是KMP,写了好长时间结果还TLE了-_-||,实际上用个简单的枚举判断就能解决.因为待验证的字符串"doge"很小.写A题的时候还被输入卡了半天.Tips1:输入至文件结尾(eof)的常用方法:     while (cin>>a)        //最常用的     while (cin.getline(s,30))    //按行读入前30个字符.空格也读入    getline(cin,s)        //和getli

NOIP模拟赛 6.29

2017-6-29 NOIP模拟赛 Problem 1 机器人(robot.cpp/c/pas) [题目描述] 早苗入手了最新的Gundam模型.最新款自然有着与以往不同的功能,那就是它能够自动行走,厉害吧. 早苗的新模型可以按照输入的命令进行移动,命令包括‘E’.‘S’.‘W’.‘N’四种,分别对应东南西北.执行某个命令时,它会向对应方向移动一个单位.作为新型机器人,它可以执行命令串.对于输入的命令串,每一秒它会按命令行动一次.执行完命令串的最后一个命令后,会自动从头开始循环.在0时刻时机器人

2017.11.25【NOIP提高组】模拟赛A组

2017.11.25[NOIP提高组]模拟赛A组 T1 3467. [NOIP2013模拟联考7]最长上升子序列(lis) T2 3468. [NOIP2013模拟联考7]OSU!(osu) T3 3472. [NOIP2013模拟联考8]匹配(match) T1 有转移方程f[i]=max{f[j]}+1,a[j]<a[i] 可以用线段树+离散化维护这个方程,因为涉及以往状态可以用主席树维护 打太丑爆空间了 Code 1 #include<cstdio> 2 #include<c

11.27 模拟赛

并没有人做的模拟赛... 出题人hx,,, T1:就是上一道矩阵乘法 数学题 T2: 一个数列中 一个区间满足,存在一个k(L <= k <= R),并且对于任意的i (L <= i <= R),ai都能被ak整除 这样的一个特殊区间 [L, R]价值为R - L 想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢 这些区间又分别是哪些呢 输出每个区间的L 思路: 用两个ST表分别求一段区间的gcd和最小值 然后可以二分答案 check的时候枚举左端点,判断在这段区间