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 |
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
一周又过去了,四校还是没考好,果然还是太弱啊。。。