zoukankan      html  css  js  c++  java
  • 【日记】12.23/【题解】CF Edu78

    12.23日记

    DP

    1. 洛谷P1280:工作日有N分钟,有K个任务,每个任务从(p_i)分钟开始,持续(t_i)分钟,每一时刻如果有多个任务要完成,则可以任选一个,但只能干一个工作,问如何选取任务,使得空暇时间最多。数据1e4。

    思路:dp[i]表示i-N分钟中,最大的空闲时间。那么从后往前遍历每个任务,这个是无后效性的,与前面的任务怎么选是无关的。那么首先对任务开始时间从大到小排序,再依次从时间n-1开始dp即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int M=1e5+20;
    struct T{
    	int st,dur;
    	bool operator<(const T &x)const{
    		return st>x.st;
    	}
    };
    struct Task{
    	int n,k,dp[M];
    	T a[M];
    	void init(){
    		scanf("%d%d",&n,&k);
    		for(int i=1;i<=k;++i)
    			scanf("%d%d",&a[i].st,&a[i].dur);
    	}
    	void run(){
    		sort(a+1,a+k+1);
    		int p=1;
    		for(int i=n;i>=1;--i)
    			if (a[p].st!=i)
    				dp[i]=dp[i+1]+1;
    			else
    				while(a[p].st==i)
    					dp[i]=max(dp[i],dp[i+a[p].dur]),++p;
    		printf("%d
    ",dp[1]);
    	}
    }task;
    int main(){
    	task.init(),task.run();
    	return 0;
    }
    

    CF Edu78

    A. Shuffle Hashing

    题意:给字符串s和h,s的hash定义为,将s随机打乱,之后加上一个任意前缀和一个任意后缀,得到的字符串即为s的hash,现询问h是否可以为s的hash。

    思路:遍历所有h的长为len(s)的区间,统计各个字符个数,如果和s的各个字符个数一样,那么就是可以。时间复杂度(O(n^2))

    #include<bits/stdc++.h>
    using namespace std;
    const int M=1e2+50;
    struct T{
    	int ans[28],now[28],len1,len2;
    	char s1[M],s2[M];
    	void init(){
    		for(int i=0;i<26;++i)
    			ans[i]=now[i]=0;
    		scanf("%s%s",s1,s2);
    		len1=strlen(s1),len2=strlen(s2);
    	}
    	inline bool check(){
    		for(int i=0;i<26;++i)
    			if (ans[i]!=now[i])
    				return false;
    		return true;
    	}
    	void run(){
    		init();
    		if(len2<len1){
    			printf("NO
    ");
    			return;
    		}
    		for(int i=0;i<len1;++i)
    			++ans[s1[i]-'a'],++now[s2[i]-'a'];
    		int p=len1;
    		while(p<len2&&!check())
    			--now[s2[p-len1]-'a'],++now[s2[p]-'a'],++p;
    		if (check())
    			printf("YES
    ");
    		else
    			printf("NO
    ");
    	}
    }t;
    int main(){
    	int T;
    	scanf("%d",&T);
    	for(int i=1;i<=T;++i)
    		t.run();
    	return 0;
    }
    

    B. A and B

    题意:给两个数a和b,第k次操作为选定一个数,将其加上k。输出使得a和b相等的最少步数。

    思路:求出a和b的差d,那么找到第一个x,使得x(x+1)/2>=d(也就是疯狂给那个小的数加),若d!=0,则此时a和b还是不一样。那么问题就转化为了,将x(x+1)/2分成两个数,使其相差d。这就要看x(x+1)/2+d是否为偶数,若为奇数,则还需要继续加,直到为偶数为止,这样才能分成整数。很容易理解一定加了不超过2次。

    时间复杂度(O(log n))

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define mid ((l+r)>>1)
    struct T{
    	int a,b,d;
    	void init(){
    		scanf("%d%d",&a,&b);
    		d=abs(a-b);
    	}
    	bool check(int x){
    		if (1LL*x*(x+1)/2>=d)
    			return true;
    		return false;
    	}
    	void run(){
    		init();
    		int l=0,r=1e5+20;
    		while(l!=r){
    			if (check(mid))
    				r=mid;
    			else
    				l=mid+1;
    		}
    		while((1LL*l*(l+1)/2+d)%2==1)
    			++l;
    		printf("%d
    ",l);
    	}
    }t;
    int main(){
    	int T;
    	scanf("%d",&T);
    	for(int i=1;i<=T;++i)
    		t.run();
    	return 0;
    }
    

    C.Berry Jam

    题意:给2n个罐头,一开始站在n和n+1之间,每个罐头为1或者2,可以选择吃掉i-n和n+1-j两个区间内的所有罐头。问最少需要吃掉多少个罐头,才能使得剩下的1和2的数量相等。

    题解:假设1个数>2个数,那么首先计算出要额外吃掉的1的个数,记为x。之后计算的时候碰到1就+1,碰到2就-1。首先计算n往左走,每走过一个就+1/-1。可以想到,如果i处为sum,j处也为sum,而且i<j,那么i一定没用。所以对于每个sum,只需要记录第一个出现的j即可。那么可以将出现的位置存入数组或者队列。对右边也一样处理。之后枚举左0右x,左1右x-1……左x右0,根据记录的位置计算出每种情况要吃的罐头的数量,取最小值即可,注意要处理左边可能没有x的情况。

    时间复杂度(O(n))

    #include<bits/stdc++.h>
    using namespace std;
    const int M=1e5+20;
    #define tr(x) (x==pd?1:-1)
    struct pl{
    	int val,num;
    	pl(int a=0,int b=0):val(a),num(b){};
    };
    struct T{
    	int n,a[M*2],num[3],pd,dt,sum,ans;
    	void init(){
    		scanf("%d",&n);
    		num[1]=num[2]=0;
    		for(int i=1;i<=2*n;++i)
    			scanf("%d",&a[i]),++num[a[i]];
    		dt=abs(num[1]-num[2]),ans=1e9+7;
    	}
    	int run(){
    		init();
    		if (num[1]==num[2])
    			return 0;
    		if (num[1]>num[2])
    			pd=1;
    		else
    			pd=2;
    		deque<pl> l,r;
    		sum=0;
    		l.push_back(pl(0,0));
    		for(int i=n;i>=1;--i){
    			sum+=tr(a[i]);
    			if (sum>l.back().val)
    				l.push_back(pl(sum,n-i+1));
    		}
    		sum=0;
    		r.push_back(pl(0,0));
    		for(int i=n+1;i<=2*n;++i){
    			sum+=tr(a[i]);
    			if (sum>r.back().val)
    				r.push_back(pl(sum,i-n));
    		}
    		while(!l.empty()){
    			if (l.front().val>dt)
    				break;
    			while(r.back().val>dt-l.front().val)
    				r.pop_back();
    			if (r.back().val+l.front().val==dt)
    				ans=min(ans,l.front().num+r.back().num);
    			l.pop_front();
    		}
    		return ans;
    	}
    }t;
    int main(){
    	int T;
    	scanf("%d",&T);
    	for(int i=1;i<=T;++i)
    		printf("%d
    ",t.run());
    	return 0;
    }
    

    D. Segment Tree

    题意:给n个点,每个点表示一个区间([l_i,r_i]),保证所有的l,r均不重复。若两个区间有重叠但不包含,则两个点之间有边。问这个图是否是一颗树。

    思路:这也算是一道数据结构题了,结果不会做………………

    区间按照l从小到大排序,每读入一个区间就将右端点和编号放入set,那么放入之前,set里剩的元素就是与之有边的元素。如果set里有右端点太小的边就删掉。显然,如果发现了超过了n-1条边,那么就直接NO。如果恰好n-1条边,再判断连通性即可。可以用并查集。

    时间复杂度(O(nlog n))

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define mid ((l+r)>>1)
    const int M=5e5+20,P=1e9+7;
    struct Seg{
    	int l,r,no;
    	Seg(int a=0,int b=0,int c=0):l(a),r(b),no(c){}
    	bool operator<(const Seg &x)const{
    		return r<x.r;
    	}
    };
    struct cmp{
    	bool operator()(Seg a,Seg b){
    		return a.l<b.l;
    	}
    };
    struct Task{
    	int n;
    	Seg a[M];
    	int road[M];
    	inline int fnd(int x){
    	    return (x==road[x])?x:road[x]=fnd(road[x]);
    	}
    	inline void merge(int u,int v){
    	    road[fnd(u)]=fnd(v);
    	}
    	void init(){
    		scanf("%d",&n);
    		for(int i=1;i<=n;++i)
    			scanf("%d%d",&a[i].l,&a[i].r),a[i].no=i,road[i]=i;
    	}
    	void run(){
    		init();
    		sort(a+1,a+n+1,cmp());
    		set<Seg> s;
    		int ans=0;
    		for(int i=1;i<=n;++i){
    			set<Seg>::iterator it=s.begin();
    			while(it!=s.end()&&it->r<a[i].l)
    				it=s.erase(it);
    			while(it!=s.end()&&it->r<a[i].r){
    				++ans,merge(a[i].no,it->no),++it;
    				if (ans>n-1){
    					printf("NO
    ");
    					return;
    				}
    			}
    			s.insert(a[i]);
    		}
    		if (ans!=n-1){
    			printf("NO
    ");
    			return;
    		}
    		unordered_set<int> st;
    		for(int i=1;i<=n;++i)
    			st.insert(fnd(i));
    		if (st.size()==1)
    			printf("YES
    ");
    		else
    			printf("NO
    ");
    	}
    }t;
    int main(){
    	t.run();
    	return 0;
    }
    
  • 相关阅读:
    skimage的安装,scikit-image
    今天开始看看brpc-baidurpc
    Silk Mobile – 缩短移动应用的测试周期
    将Controller中的数据传递到View中显示
    常用排序算法总结(二) ---- 插入排序,快速排序
    关于二叉搜索树及三种树遍历的特点
    poj
    元数据(meta-data)
    《UNIX环境高级编程》笔记--文件访问权限和新文件、目录所有权
    UVA 674 Coin Change 换硬币 经典dp入门题
  • 原文地址:https://www.cnblogs.com/diorvh/p/12089254.html
Copyright © 2011-2022 走看看