[20180816]校内模拟赛

T1 清理(clear)

问题描述

小 C 最近自己开发了一款云盘软件,目前已有??个用户。小C 的云盘上的文件会被后台分成两种类型,活动

文件和非活动文件,活动文件即可能常用的文件,会被放在高速服务器上给用户提供高速下载服务。用户

上传一个文件时,这个文件会被设置为活动文件。由于高速服务器内存大小有限,小 C 需要把一些文件

设为非活动文件,有以下两种设置方式:1.把上传时间前??早的文件全部设为非活动文件;2.把第??个用户上

传的文件全部设为非活动文件。注意这两种方式操作的对象都是所有文件,也就是说非活动文件可以被重

复设为非活动文件。

现在小 C 需要你写一个程序来维护所有文件的类型,并在每次操作后输出当前活动文件的数量,假设一开

始没有任何文件。

输入格式

第一行两个正整数??,??,其中??表示操作数。

接下来??行,每行两个正整数??????,??,若?????? = 1,表示第??个用户上传了一个文件;若?????? = 2,表示将第??个

用户上传的文件全部设为非活动文件;若?????? = 3,表示将上传时间前??早的文件设为非活动文件,保证此时

??不超过当前总文件数。

输出格式

输出??行,表示每次操作结束后的活动文件数量。

样例

样例输入


 3 5
 1 1
 1 2
 1 3
 2 1
 3 2 

样例输出


 1
 2
 3
 2
 1 

数据范围

对于 100%的数据,??,?? ≤ 3 ? 10^5。

Solution

维护每个用户的总文件数以及非活动文件数(非活动文件必然是该用户前k个上传的文件)记录第i个上传的文件是上传用户的第几个文件进行将前x个文件设为非活动的操作时,假设之前进行过的这个操作最大的x为max,只要处理[max+1,x]内的文件,更新用户的非活动文件数顺便计算答案即可,每个文件最多被处理一次,总时间复杂度O(m).

PS:然而,我却打了一个O(mlogm)的。

#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
inline 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-'0';ch=getchar();}
    return x*f;
}
#define MN 300005
#define lowbit(x) (x&-(x))
int n,m,opt,x,del,cnt;
int a[MN],sum,last[MN],total[MN];
std::vector<int> pos[MN];
int t[MN];
inline void C(int x){for(;x<=m;x+=lowbit(x)) t[x]++;}
inline int G(int x){int res=0;for(;x;x-=lowbit(x)) res+=t[x];return res;}
int main(){
    freopen("clear.in","r",stdin);
    freopen("clear.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=m;i++){
        opt=read(),x=read();
        if(opt==1){
            a[x]++;sum++;total[x]++;
            pos[x].push_back(++cnt);
        }
        else if(opt==2){
            for(int j=last[x]+1;j<=total[x];j++) C(pos[x][j-1]);
            last[x]=total[x];
            sum-=a[x];a[x]=0;
        }
        else if(opt==3){
            del=std::max(del,x);
        }
        printf("%d\n",sum-del+G(del));
    }
    return 0;
}

T2 分组(group)

问题描述

有??个人,你要把他们分成若干组,第??个人希望自己所在组内人数不少于????,求最多分成多少组。

输入格式

第一行一个正整数??。

接下来??行,每行一个正整数,表示????。

输出格式

输出一个整数,表示答案,数据保证有解。

样例

样例输入


5
2
1
2
2
3

样例输出


2

数据范围

对于 100%的数据,n≤ 10^6 。

Solution

考虑最大的ai所在的组,这个组的限制只有人数不少于这个最大的ai,其他成员并没有影响,故将尽量大的

ai放到这个组内显然最优如果这个组贪心的恰好取最大的ai个,容易构造出反例(3 3 3 3 1 1)将ai从小到大

排序,不难发现,一定存在一种最优方案满足每组是排序后的一个区间用f[i]表示排序后前i个人最多分成几

组,那么f[i]=max(f[j]+1) (j=0~i-ai-1)用前缀max可以优化到O(n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MN 1000005
#define ll long long
#define inf (1e9)
#define lowbit(x) (x&(-x))
inline ll read(){
    ll 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-'0';ch=getchar();}
    return x*f;
}
ll n,a[MN],res;
ll t[MN],f[MN];
inline void rw(ll &x,ll y){if(y>x) x=y;}
inline void C(int y,ll x){rw(t[y],x);for(;y<=n+1;y+=lowbit(y))rw(t[y],x);}
inline ll G(int x){res=-inf;for(;x;x-=lowbit(x)) rw(res,t[x]);return res;}
int main(){
    freopen("group.in","r",stdin);
    freopen("group.out","w",stdout);
    n=read();
    register int i;
    for(i=1;i<=n;i++) a[i]=read();
    std::sort(a+1,a+n+1);
    C(1,0);
    for(i=1;i<=n;i++){
        if(i+1<=a[i]) continue;
        else{
            f[i]=G(i+1-a[i])+1;
            C(i+1,f[i]);
        }
    }
    printf("%lld\n",f[n]);
    return 0;
}

T3 排序(sort)

问题描述

Bob 有一个??个数的数组,下标为0~?? ? 1且数组里恰好包含了0~?? ? 1各一次。 Bob 打算拿这个数组去和

Alice 玩一个游戏,Alice 和 Bob 轮流操作??轮,每轮两人各操作一次,Alice 先,操作时需要选择数组中的

两个元素(可以重复)交换,一旦数组变成有序的,Bob 就获得胜利且游戏结束,若??轮结束后 Bob 都没

有获胜,Alice 获胜。

可惜 Alice 没空,并不想陪 Bob 玩,Alice 告诉 Bob,如果有第??轮,他就选择下标为????和????的元素交换。

Bob 想知道,自己最少能用几轮获胜。

输入格式

第一行一个正整数??。

第二行??个非负整数,表示 Bob 的数组。

第三行一个正整数??。

接下来??行,每行两个非负整数????,????。

输出格式

输出一个整数,表示答案,如果 Bob 不能获胜,输出?1。

样例

样例输入


2
0 1
2
0 0
0 0

样例输出


0

数据范围

对于 100%的数据,n ≤ 2* 10^5 ,n≤m≤ 6*10^5 。

Solution

考虑依次进行两个交换操作swap(a,b);swap(c,d);(假设a<b,c<d)

如果abcd是四个不同的元素或a=c且b=d,显然变成swap(c,d);swap(a,b);结果不变

如果b=c或a=d,我们可以把操作看成swap(x,y);swap(y,z);,此时把操作改成swap(y,z);swap(x,z);结果不变

对于两个相邻操作,我们可以修改前一个操作再将这个操作放到后面,结果不变

本题中,操作序列可以看成ABABAB...,A代表Alice的操作,B代表Bob的操作,其中Bob的操作是可以我们

自己决定的,我们可以通过交换相邻操作并修改Bob的操作来让序列变为AAA...BBB...

由于我们可以把一次交换操作再交换回来,故如果第i轮能获胜,第i+1轮也能获胜

二分答案ans,模拟Alice的前ans轮操作,只要计算Bob至少需要几次操作才能完成排序即可判断答案合法性

让i向ai连边,对于每个大小为size的环,需要size-1次操作来完成排序

答案显然不超过n,总时间复杂度不超过O(nlogn)

#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
inline 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-'0';ch=getchar();}
    return x*f;
}
#define MN 200005
#define MM 600005
#define mid (l+r>>1)
int a[MN],n,m,optx[MM],opty[MM];
int change[MN];
bool vis[MN];
int getsize(int x){
    int size=1;vis[x]=1;
    for(int i=change[x];i!=x;i=change[i]) vis[i]=1,size++;
    return size;
}
int getstep(){
    int res=0;
    memset(vis,0,sizeof vis);
    for(int i=1;i<=n;i++){
        if(!vis[i]) res+=getsize(i)-1;
    }
    return res;
}
bool check(int x){
    register int i;
    for(i=1;i<=n;i++) change[i]=a[i];
    for(i=1;i<=x;i++) std::swap(change[optx[i]],change[opty[i]]);
    if(getstep()<=x) return true;
    else return false;
}
int main(){
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    register int i;
    n=read();
    for(i=1;i<=n;i++) a[i]=read()+1;
    m=read();
    for(i=1;i<=m;i++) optx[i]=read()+1,opty[i]=read()+1;
    int l=0,r=m,ans;
    while(l<=r){
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(l>m) return 0*puts("-1");
    else return 0*printf("%d\n",ans);
}

T4 附加题

这是题面

Solution

先考虑M=0的情况,即对每个i求到其他所有点的距离之和

先考虑一个暴力的做法,用f[i][x]表示以i为根的子树x内所有点到x的距离之和,枚举一个儿子y转移,

f[i][x]+=f[i][y]+size[y]*w

对每个i做一遍DP,时间复杂度为O(n^2)考虑两个相邻的点i和j,分别以i为根和以j为根,不同的子树只有子

树i和子树j.假设已经求出了f[i][x],我们只需要重新计算f[j][i]和f[j][j]就能求出所有f[j][x]

具体地说,我们让f[i][i]减去从j转移来的答案,得到f[j][i],再用f[j][i]转移到f[i][j]得到f[j][j]

我们可以先求出所有f[1][x],接着dfs一遍整棵树,维护以当前dfs到的点为根的DP值,即可O(n)求出所有答

案对于M<=15的情况,距离异或上M只有二进制下后4位有影响,我们DP状态中再加一维,f[i][x][k]表示

以i为根的子树x内到x距离模16为k的点的距离之和,后面的换根操作同理,总时间复杂度O(nM)

#include<bits/stdc++.h>
using namespace std;
inline 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-'0';ch=getchar();}
    return x*f;
}
#define MM 15
#define MN 100005
struct edge{int to,w,nex;}e[MN<<1];int cnt,hr[MN];
inline int ins(int f,int t,int w){
    e[++cnt]=(edge){t,w,hr[f]};hr[f]=cnt;
    e[++cnt]=(edge){f,w,hr[t]};hr[t]=cnt;
}
int n,M;
int f[MN][MM+5],g[MN][MM+5],ans[MN];
inline void dp(int fa,int x){
    g[x][0]=1;register int i,j;
    for(i=hr[x];i;i=e[i].nex)if(e[i].to^fa){
        dp(x,e[i].to);
        for(j=0;j<=MM;j++){
            f[x][(j+e[i].w)&MM]+=f[e[i].to][j]+g[e[i].to][j]*e[i].w;
            g[x][(j+e[i].w)&MM]+=g[e[i].to][j];
        }
    }
}
inline void dfs(int fa,int x){
    register int i,j;
    for(i=0;i<=MM;i++) ans[x]+=f[x][i]-g[x][i]*i+g[x][i]*(i^M);
    for(i=hr[x];i;i=e[i].nex)if(e[i].to^fa){
        for(j=0;j<=MM;j++){
            f[x][(j+e[i].w)&MM]-=f[e[i].to][j]+g[e[i].to][j]*e[i].w;
            g[x][(j+e[i].w)&MM]-=g[e[i].to][j];
        }
        for(j=0;j<=MM;j++){
            f[e[i].to][(j+e[i].w)&MM]+=f[x][j]+g[x][j]*e[i].w;
            g[e[i].to][(j+e[i].w)&MM]+=g[x][j];
        }
        dfs(x,e[i].to);
        for(j=0;j<=MM;j++){
            f[e[i].to][(j+e[i].w)&MM]-=f[x][j]+g[x][j]*e[i].w;
            g[e[i].to][(j+e[i].w)&MM]-=g[x][j];
        }
        for(j=0;j<=MM;j++){
            f[x][(j+e[i].w)&MM]+=f[e[i].to][j]+g[e[i].to][j]*e[i].w;
            g[x][(j+e[i].w)&MM]+=g[e[i].to][j];

        }
    }
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=read();M=read();register int ai,bi,ci,i;
    for(i=1;i<n;i++){
        ai=read(),bi=read(),ci=read();
        ins(ai,bi,ci);
    }
    dp(0,1); dfs(0,1);
    for(i=1;i<=n;i++) printf("%d\n",ans[i]-M);
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/9495902.html

时间: 2024-10-08 12:48:21

[20180816]校内模拟赛的相关文章

2017.6.11 校内模拟赛

题面及数据及std(有本人的也有原来的) :2017.6.11 校内模拟赛 T1 自己在纸上模拟一下后就会发现 可以用栈来搞一搞事情 受了上次zsq 讲的双栈排序的启发.. 具体就是将原盘子大小copy一下排个序 用两个指针维护两个数组(原数据 和 排序后的数据), 即分为1数据和2数组 将小于1指针指向的数据的2数组中的数据全部压入栈中 后进行消除, 将栈栈顶元素与当前1数组中的1指针指向的元素进行比较 相同则消除 后重复过程 直至指针超过N 后判断一下是否两个指针都超过了N... #incl

校内模拟赛 Zbq&#39;s Music Challenge

Zbq's Music Challenge 题意: 一个长度为n的序列,每个位置可能是1或者0,1的概率是$p_i$.对于一个序列$S$,它的得分是 $$BasicScore=A\times \sum_{i=1}^{n}{S_i} \tag{1}$$ $$ combo(i)=\left\{ \begin{aligned} &S_i & &i=1 \\ &combo(i-1)+1 & &i\neq 1 ~\mathrm{and}~ S_i=1 \\ &

校内模拟赛T1大美江湖

这就是一个模拟题,注意1234分别对应左右上下横坐标和纵坐标的判断就好了 题解: 需要注意的是,向上取整ceil函数是对于一个double值返回一个double值,也就是说在ceil里面的类型一定要是double,否则会炸 代码: #include<cstdio> #include<iostream> #include<cstdlib> #include<cmath> #include<cstring> #include<string>

校内模拟赛:确定小组

  [问题描述] 有n个人坐成一排,这n个人都在某一个小组中,同一个小组的所有人所坐的位置一定是连续的. 有一个记者在现场进行采访,他每次采访都会询问一个人其所在的小组有多少人,被询问的每个人都给出了正确的答案,但是由于时间仓促,记者不一定询问了每个人,我们记录一个长度为n的答案序列,序列的第i个数表示第i个人的回答,如果为0则表示记者没有询问过这个人. 记者发现,对于一些情况,他可以唯一确定这排所有人的分组,而对于另外一些情况则不能,于是记者开始好奇,对于某一个答案序列,他能不能做到这一点,如

校内模拟赛20170604

香蕉锤--双向链表 #include<iostream> #include<cstdio> using namespace std; inline int read(){ int num=0,t=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();} while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();} ret

5.13 校内模拟赛

... 果然是dalao们做难题做多了后 简单题水不起来了吗.. 5.13解题报告 300分 T1写了差不多30~40分钟 T2写了不到5min (当时怀疑有坑..) T3推了大概1个多小时的式子, 然后又加上调试差不多一个半小时 时间分配就这样..感觉T2出的太过简单了..直接是个模板 T1 并查集 + 乱搞 T2 快速幂模板 T3 Dp T1 : codevs 2796 最小完全图 二次联通门 : codevs 2796 最小完全图 /* codevs 2796 最小完全图 并查集 + 乱搞

校内模拟赛(20170924)

四校很丧,但是学长的题目更简单 lrb学长的题目为什么都要倒着切,不懂QAQ ----------------我是分割线---------------- T1:个人卫生综合征 每天BBS都要从家里经过城市中的一段路到学校刷五三.城市中一共有n个路口和m条双向道路,每条双向道路都连接着两个路口ai.bi且有一定的时间花费vi.BBS家编号为1,学校编号为n.今天,BBS由于个人卫生综合征导致他很迟才离开家,他想用膜法改变k条道路的长度使通过其的时间花费vi变为0.现在他问你改变道路长度之后他到学校

校内模拟赛(20170921)

救命啊,救命啊!学长出丧题啦!!! 学长他们压榨我们的劳动力,然后带着我们的成绩跑了,无奈的我们只好玩命的调程序,把学长留给我们的丧题做完(划掉) 65分的rank 1跑路. ----------------我是分割线---------------- T1:粉饰(decorate) [题目描述] 小D有一块被分为n*m个格子的矩形鱼片.为了装饰鱼片,小D决定给每个格子上色.由于小D很喜欢红白,所以小D给每个格子涂上了红色或白色,第i行第j列的格子颜色记为c[i,j].涂完之后,小D想评估这块鱼片

校内模拟赛(20170917)

这套题目也是比较恶心的,....都是奇技淫巧的说. ----------------我是分割线------------------ T1:消消乐(tet) [题目描述] 有一个2n个数字排成一列,这个数列中由1..n中的数字组成,每个数字都恰好出现两次.每个回合可以交换相邻两个数字,交换以后如果有两个相同的数字相邻,那么将这两个数字消除,并将这两个数字的两端拼起来:如果拼接后的数列仍有相同的数字相邻,那么将引发连锁反应直到没有两个相同数字相邻.现在你想知道最少需要几个回合可以消除所有的数字. [