校内集训(20170910)

T1打挂怒丢rank 1(人生要不要这么残酷)

果然AK一次之后就会砸

刚好碰到洛谷打卡大凶(划掉)

——————————————————我是分割线————————————————————

T1:CCT

最近学校又发了n本五三题霸,BBS看到后十分高兴。但是,当他把五三拿到手后才发现,他已经刷过这些书了!他又认真地看了一会儿,发现新发的这些五三是2017版的,而他刷的是2016版的。现在他想找出所有他没有刷过的题来刷。每本五三都有m道题,并且它的特征(即它和去年版本的五三的差距)可以用一个m位二进制数来代表,二进制位上的1代表该题不同,0代表该题相同。比如4(100)就代表题目3和去年的有不同、5(101)就代表题目1和题目3和去年的有不同。而BBS热衷于给自己找麻烦,他要选择连续一段的几本五三一起刷,并且要求,所有选择的五三的特征中的所有k位中每一位出现1的次数都相同。他又想去刷最多的书,请你告诉他,他最多能刷多少本书?

输入格式:

第一行为两个整数 n、m,接下来的n行为 n 个整数,表示每本五三的特征。

输出格式:

一个整数,表示BBS最多能刷几本书。


样例输入


样例输出


7 3

7

6

7

2

1

4

2


4

样例解释:

这7本五三的特征分别为111,110,111,010,001,100,010。选择第3本至第6本五三,这些五三的特征中每一位都出现了2次1。当然,选择第4本到第6本也是可以的,这些五三的特征中每一位都出现了1次1。只是这样子BBS刷的书的数量就少了,他就会不高兴。

数据范围:

对于 100%的数据:1<=n<=100000,1<=k<=30。

——————————————————我是分割线————————————————————

emmm第一题是最难的,也不知道出题人怎么想的。

其实就是数组hash而已啦,我们每次读入一个特征值,然后把它转为二进制放到前缀和数组里面去,然后我们hash这一整个数组,注意:hash的时候数组的每一位要减去整个数组的最小值,比如1 1 2而2 2 3的hash值应该是一样的。

那么很快我们就要考虑到一个性质,假如之前已经出现了第i个数组的hash值,且是第j个数组的hash值。那么我们的答案就要对i-j取个max,然后就是hash的事情啦QAQ

不过有一个注意事项。如果整个序列都是答案,那么我们需要从第0位开始hash(也就是把sum[0]数组也放进hash表内)

变量打错了怒失100啊(~~~~~~~~~~~~~抓狂)

下面贴代码

#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define MN 200005
#define mod 1000007
using namespace std;
int n,m,tot,num,ans,head[mod+1],sum[MN][31],a[MN];
struct ddd{
    int aa[31];
    int opt,next;
}hash[MN];
void hashh(int *q,int nn){
    long long qaq=0;
    int minn=1000005;
    for(int i=0;i<m;i++)minn=min(minn,q[i]);
    for(int i=0;i<m;i++)qaq=qaq+(1ll*(1<<i))*(1ll*(q[i]-minn));
    int tmp=qaq%mod;
    for(int i=head[tmp];i;i=hash[i].next){
        bool find=true;
        for(int j=0;j<m;j++)
            if(hash[i].aa[j]!=q[j]-minn){find=false;break;}
        if(find){ans=max(ans,nn-hash[i].opt);return;}
    }
    hash[++num].next=head[tmp];head[tmp]=num;hash[num].opt=nn;
    for(int i=0;i<m;i++)hash[num].aa[i]=q[i]-minn;
}
int main(){
    freopen("cct.in","r",stdin);
    freopen("cct.out","w",stdout);
    scanf("%d%d",&n,&m);
    hashh(sum[0],0);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){
        int tmp=a[i];
        bool fd=true;
        for(int j=0;j<m;j++){
            sum[i][j]=sum[i-1][j];
            if(tmp&1)sum[i][j]++;
            if(!sum[i][j])fd=false;
            tmp=tmp==0?tmp:tmp/2;
        }
        hashh(sum[i],i);
    }
    printf("%d\n",ans);
    fclose(stdin);
    fclose(stdout);
}

——————————————————我是分割线————————————————————

T2:MHM

LGL今天一共要上n节课,这n节课由0标号至n。由于过度劳累,除了第0节课和第n节课,LGL还打算睡上m节课,所以他做了一个睡觉计划表。通过小道消息,LGL得知WQ今天会在学校中检查,所以他想少睡k节课。但是由于某些原因,他又想使相邻的两节睡觉的课之间上的课数量的最小值最大。由于他很困,所以他请你来帮他计算这个值。

 

输入格式:

第一行为三个整数 n、m、k,接下来的m行为m个整数ai,表示睡觉计划表中LGL想要睡觉的课。

输出格式:

一个整数,表示题目所求的值。


样例输入


样例输出


25 5 2

14

11

17

2

21


3

样例解释:

选择第2节和第14节不睡觉,这样子相邻的两节睡觉的课之间上的课数量的最小值为3,即第17节和第21节之间和第21节到第25节之间。没有答案更大的删除方案。

数据范围:

对于100%的数据:1<=n<=109,1<=k<=m<=50000,0<ai<n。

——————————————————我是分割线————————————————————

说实在的,我真的没有见到过比这个更裸的跳石头问题了。(果然是NOIP普及-难度)

直接上代码吧,不过我的答案-1是因为我的算法是一段不取一段取的,要把取的那个端点去掉。

#include<cstdio>
#include<algorithm>
#define MN 50005
using namespace std;
int a[MN],n,m,k;
bool pd(int num){
    int last=0,sum=0;
    for(int i=1;i<=m+1;i++){
        if(a[i]-a[last]<num)sum++;
        else last=i;
        if(sum>k)return false;
    }
    return true;
}
int main(){
    freopen("mhm.in","r",stdin);
    freopen("mhm.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)scanf("%d",&a[i]);a[m+1]=n,a[0]=0;
    sort(a,a+m+2);
    int l=0,r=n,ans;
    while(l<=r){
        int mid=l+r>>1;
        if(pd(mid))l=mid+1,ans=mid;
        else r=mid-1;
    }
    printf("%d\n",ans-1);
    fclose(stdin);
    fclose(stdout);
}

——————————————————我是分割线————————————————————

T3:AAFA

YYH有n道题要做。每一道题都有一个截止日期t,只要在该日期之前做完,他的父亲LRB就会奖励他w元钱。令人惊讶的是,每一道题他都只需要1秒来做。请问他最多能从父亲那里拿到多少钱?

输入格式:

第一行为一个整数 n,接下来的n行每一行都有两个数ti和wi,分别表示第i题的截止日期和奖励。

输出格式:

一个整数,表示YYH的最大获利。


样例输入


样例输出


3

2 10

1 5

1 7


17

样例解释:

第1秒做第3道题,第2秒做第1道题。

数据范围:

对于 100%的数据:1<=n、ti 、wi <=100000。

——————————————————我是分割线————————————————————

这显然是贪心,显然对于一个可取的区间,我们肯定从大往小了取,只不过脑子抽了的我写了个线段树,而且还没考虑到种种情况,大概我是假人。

直接用pq(优先队列)就好了,把按照时间排序完的数组一个个插到队列里判断一下。

最后答案就是优先队列中所有元素的和。很简单。

上代码。

#include<cstdio>
#include<queue>
#include<algorithm>
#define MN 100005
using namespace std;
struct eee{
    int t,w;
}a[MN];
int n,sz;long long ans;
priority_queue<int,vector<int>,greater<int> >q;
bool cmp(eee b,eee c){return b.t<c.t;}
int main(){
    freopen("aafa.in","r",stdin);
    freopen("aafa.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].t,&a[i].w);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++){
        sz=q.size();
        if(sz<a[i].t)q.push(a[i].w);
        else if(sz==a[i].t&&q.top()<a[i].w)q.pop(),q.push(a[i].w);
    }
    while(!q.empty())ans+=q.top(),q.pop();
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
}

——————————————————我是分割线————————————————————

T4:ZZI

YYH拿到了父亲给的钱欣喜若狂,把这些钱拿来造了n栋房子。现在他要给这些房子通电。他有两种方法:第一种是在房间里搭核电发电机发电,对于不同的房子,他需要花不同的代价Vi;,第二种是将有电的房子i的电通过电线通到没电的房子j中,这样子他需要花的代价为aij。他现在请你帮他算出他最少要花多少钱才能让所有的房子通上电。

输入格式:

第一行为一个整数 n。接下来的n行为 n 个整数vi,再接下来的n行每行n个数,第i行第j列的数表示aij

输出格式:

一个整数,表示最小代价。


样例输入


样例输出


4
5

4

4

3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0


9

样例解释:

在第4栋房子造核电发电机,再将其他三栋房子通过电线连向它。

数据范围:

对于 100%的数据:1<=n<=300,1<=vi,aij<=100000,保证aii=0,aij=aji

——————————————————我是分割线————————————————————

这肯定是生成树啊,上次T4没有写出生成树的我显然不会再犯这种错误了(插一句,对于多次查询区间的某一个特征的总和,然后要你求出得出区间的所有数的最小代价,这种题目只要不是太坑,就是MST(最小生成树)啦)至于怎么建发动机,自然就是多搞一个0号点,然后向每一个点连边啦,。

上代码咯

#include<cstdio>
#include<algorithm>
using namespace std;
int n,x,y,num,ans,tot,fa[305];
struct edge{
    int u,v,w;
}g[100005];
int getfa(int q){return !fa[q]?q:fa[q]=getfa(fa[q]);}
bool cmp(edge a,edge b){return a.w<b.w;}
int main(){
    freopen("zzi.in","r",stdin);
    freopen("zzi.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&x),g[++num].u=0,g[num].v=i,g[num].w=x;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            scanf("%d",&x);
            if(j>i)g[++num].u=i,g[num].v=j,g[num].w=x;
        }
    sort(g+1,g+num+1,cmp);
    for(int i=1;i<=num&&tot<n;i++){
        x=getfa(g[i].u),y=getfa(g[i].v);
        if(x!=y){
            fa[x]=y;
            ans+=g[i].w;
            tot++;
        }
    }
    printf("%d\n",ans);
    fclose(stdin);
    fclose(stdout);
}

emmmm

一周又过去了,四校还是没考好,果然还是太弱啊。。。

时间: 2024-09-30 09:31:32

校内集训(20170910)的相关文章

校内集训(20170906)

emmmm又是200.. 我大概是永远不会AK?T3我都想到算法了..(掀桌~) 切入正题吧. ------------------我是分割线------------------ T1:切糕(cut) [问题描述] 小R意外获得了一块切糕,他准备把切糕分给n个小伙伴.切糕的形状是一个底边长为a,高为b的等腰三角形.小R打算横着或竖着切n-1刀把切糕切成面积相等的n块分给小伙伴,请你告诉他要在哪些地方切. [输入格式] 输入文件cut.in 输入包含四个整数n,a,b,c,表示要切成n块,切糕的三

校内集训 miku set模拟冒泡排序 逆序对

题意 给你一个长为\(n\)的序列,进行\(m\)次操作,每次对一个区间进行排序,求最后的序列\((n <= 1500, m <= 1e6)\) 这道题目思维难度挺大的 对于一个序列,排序的本质就是消除里面的所有逆序对 考虑冒泡排序的过程,每次也是交换\(a[i]>a[i+1]\)这个逆序对 这样子交换最多有\(n^2\)次 那么我们可以用一个数据结构模拟冒泡排序交换逆序对这个过程 用一个\(set\)维护所有逆序对的位置 每次暴力删除区间内所有逆序对 再把新产生的逆序对加入\(set\

[CSP校内集训]贪吃蛇(阿尔法-贝塔剪枝)

题目 有两条蛇(1号蛇和2号蛇)在n行m列的地图上,地图上有障碍物.一条蛇碰到蛇身/障碍物/边界就会死.蛇身会不断长长--可以理解为蛇尾位置不会变,蛇只会向前伸展不会缩尾巴.两条蛇都绝顶聪明,如果自己能赢,一定会尽量快地赢;如果自己会输,一定会死得尽量晚.给出初始局面,两蛇轮流走,每次可以且必须向上下左右移动一格.1号蛇先走,请告诉我谁会在多少回合时赢.\((n,m\leq 20)\)且\(0\)的数量不超过\(50\) \(\alpha - \beta\)剪枝 AlphaBeta剪枝算法是一个

[CSP校内集训]attack(DAG支配树)

题意 给一个DAG,多次询问,每次给定\(k\)个点,求1到这些点的必经点的交集大小 思路 支配树裸题,建好DAG的支配树后\(k\)个点LCA的深度即为答案 Code #include<bits/stdc++.h> #define N 100005 using namespace std; int n,m,q; int rd[N],f[N][18],dep[N]; struct Edge { int next,to; }edge[N<<1],edge1[N<<1];i

[CSP校内集训]tree(期望DP)

题意 给一颗树,从1节点出发,走每条边的概率相同且耗时为1,求每个点第一次被遍历到的期望时间(\(t_1=1\)) 思路 在树上只有两种移动方式:从儿子到父亲,从父亲到儿子 假设从\(rt\)走到\(v\)的期望代价为\(dow_i\),从\(i\)走到\(rt\)的期望代价为\(val_i\) 假设从\(rt\)转移到\(v\),\(rt\)的度数为\(k\),\(rt\)的父亲为\(fa\),则: \[dow_v = \frac{1}{k} + \sum_{son}^{son\neq v}

[CSP校内集训]rank

题意 给出一个字符串后缀排序之后的数组\(sa_i\),求原字符串(字典序最小),无解输出-1 思路 显然从\(rk_1\)开始填字符是可以保证字符单调不降的 找到\(sa\)值相邻的两个位置,现在需要知道\(rk_i\)和\(rk_{i+1}\)是否可以填相邻字符:当它们填相同字符时需要比较后一位,如果相对关系相同则可行(因为后一位默认已经成立) 举个栗子:\(4,2,3,1\),查看4能不能和3填同一个字符,则判断后一位2和1:而\(4>3 \&\& 2>1\),所以可以相

[CSP校内集训]矩形面积交(树状数组)

题意 给\(n\)个互不相交的矩形,再给\(m\)个询问,每次给一个矩形求它与这\(n\)个矩形的面积交 思路 自己写的太丑了导致DEBUG了一个半小时qwq 一对矩形的交可以拆分成二维前缀和形式下的矩形的交,于是变成判断16次矩形的交(不想画图...只想口胡) 这些矩形都有\(x_0=0,y_0=0\),即左下角为坐标原点,于是一个矩形可以只用右上角的坐标表示: 对于一个询问的矩形\((x,y)\)和另一个矩形\((x_i,y_i)\),它们的交为\(min(x,x_i)\times min(

[CSP校内集训]pestc(拓扑排序)

题意 给一个边带权的有向图,可以花费边权使得一条边反向:通过翻转边让原图变成一个DAG,要求是所有花费中的最大值最小\(,(n,m\leq 200000)\),保证无重边和自环 解法1 考场上没看出来性质,于是口胡了一个乱搞做法 连好边后直接对原图进行一遍拓扑排序,由于原图不是DAG,所以会有无法入队的环存在:如果当前队列为空而有点没有被遍历到,那么就强行选择一个点将连向它的边翻转: 具体的,我们选择\((max(\) 连向\(i\)的边 \())\)最小的\(i\),由于翻转了连向\(i\)的

20170910校内训练

CCT 最近学校又发了n本五三题霸,BBS看到后十分高兴.但是,当他把五三拿到手后才发现,他已经刷过这些书了!他又认真地看了一会儿,发现新发的这些五三是2017版的,而他刷的是2016版的.现在他想找出所有他没有刷过的题来刷.每本五三都有m道题,并且它的特征(即它和去年版本的五三的差距)可以用一个m位二进制数来代表,二进制位上的1代表该题不同,0代表该题相同.比如4(100)就代表题目3和去年的有不同.5(101)就代表题目1和题目3和去年的有不同.而BBS热衷于给自己找麻烦,他要选择连续一段的