zoukankan      html  css  js  c++  java
  • BUPT2017 wintertraining(15) #1 题解

    拖了一周才完成的题解,抛出一个可爱的表情 (っ'-')╮ =͟͟͞͞❤️。对我来说E、F比较难,都是线段树的题,有点久没写了。

    A - Infinite Sequence

    CodeForces - 675A

    公差为c,首项为a的等差数列是否有一项等于b。

    注意c为0的情况。

    #include<cstdio>
    long long a,b,c;
    int main() {
    	scanf("%lld%lld%lld",&a,&b,&c);
    	if(c==0&&b==a||c&&(b-a)%c==0&&(b-a)/c>=0)puts("YES");
    	else puts("NO");
    	return 0;
    }
    

    B - Modular Inverse

    ZOJ - 3609 

    求逆元。以前写过题解,http://www.cnblogs.com/flipped/p/5193777.html

    C - Prison rearrangement

    POJ - 1636

    两个监狱各有m个犯人,现在各拿出相同个犯人交换,给定两个监狱有那些犯人是不能在一个监狱的,求最多交换多少人,且不超过m/2。

    先用dfs计算出二分图的联通子图的左边的个数和右边的个数,然后用动态规划:

    (dp[i][j])表示左边共选了i个人,右边j个人交换是否可行。

    (dp[i][j]|=dp[i-le[i]][j-re[i]])

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #define ll long long
    #define N 305
    using namespace std;
    int s,m,r,g[N][N];
    int cnt,le[N],re[N];
    int dp[N][N];
    bool vis[2][N];
    void dfs(int v,int c){
    	vis[c][v]=1;
    	if(c)le[cnt]++;else re[cnt]++;//连通图左边、右边的点个数
    	for(int i=1;i<=m;i++)//c==1,左边走到右边
    		if((c&&g[v][i]||!c&&g[i][v])&&!vis[!c][i]) dfs(i,!c);
    }
    int main() {
    	scanf("%d",&s);
    	for(int cas=1;cas<=s;cas++){
    		for(int i=0;i<N;i++)le[i]=re[i]=vis[0][i]=vis[1][i]=0;
    		memset(g,0,sizeof g);
    		memset(dp,0,sizeof dp);
    		cnt=0;
    		scanf("%d%d",&m,&r);
    		for(int i=1;i<=r;i++){
    			int x,y;
    			scanf("%d%d",&x,&y);
    			g[x][y]=1;
    		}
    		for(int i=1;i<=m;i++)
    		for(int c=0;c<2;c++)
    			if(!vis[c][i]){
    				cnt++;
    				dfs(i,c);
    			}
    		dp[0][0]=1;
    		for(int i=1;i<=cnt;i++)
    		for(int j=m/2;j>=le[i];j--)
    		for(int k=m/2;k>=re[i];k--)
    			dp[j][k]|=dp[j-le[i]][k-re[i]];
    
    		int i=m/2;
    		while(i&&dp[i][i]==0)i--;
    		printf("%d
    ",i);
    	}
    	return 0;
    }
    

    D - Task Schedule

    HDU - 3572

    n个任务,m个机器,每台机器每天只能做一个任务,每个任务允许时间范围([s_i,e_i]),需要时间长度(p_i)。求能否安排所有任务使得它们都能完成。

    网络流最大流:

    构图:1. 源点s->任务,容量p。2. 日子->汇点t,容量m。3. 任务->允许范围的日子,容量1。

    用isap可以直接过,dinic我超时了,当前弧优化后就过了。

    优化后的dinic:

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    using namespace std;
    #define N 1005
    #define M 2000005
    #define INF 0x7fffffff
    struct edge{
    	int to,next,w;
    }e[M];
    int head[N],g[N],cnt;
    int d[N],ans,tans;
    int st,ed;
    void add(int u,int v,int w){
    	e[cnt]=(edge){v,head[u],w};head[u]=cnt++;
    	e[cnt]=(edge){u,head[v],0};head[v]=cnt++;//反向弧
    }
    int bfs(){
    	memset(d,-1,sizeof d);
    	queue<int>q;
    	q.push(st);
    	d[st]=0;
    	while(!q.empty()){
    		int i,k=q.front();
    		q.pop();
    		for(i=head[k];~i;i=e[i].next){
    			int v=e[i].to;
    			if(e[i].w>0&&d[v]==-1){
    				d[v]=d[k]+1;
    				q.push(v);
    			}
    		}
    	}
    	return d[ed]>0;
    }
    int dinic(int k,int low){//在k节点有流量low,到终点ed的最大流
    	if(k==ed||low==0)return low;
    	int a,res=0;
    	for(int &i=g[k];~i;i=e[i].next){//这个g就是当前弧优化
    		int v=e[i].to;
    		if(d[v]==d[k]+1&&e[i].w>0&&(a=dinic(v,min(low,e[i].w)))){
    			res+=a;//当前这条弧送了a流量到终点
    			low-=a;
    			e[i].w-=a;
    			e[i^1].w+=a;
    			if(!low) break;//如果k相连的前面几条弧已经把low那么多流量送到终点,就不需要后面的弧了,下次访问k时就从现在的g[k]这条弧开始增广。
    		}
    	}
    	return res;
    }
    void solve(){
        ans = 0;
        while(bfs()) {
        	memcpy(g,head,sizeof g);//重新计算过层次图后再初始化当前弧g
        	while(tans=dinic(st, INF)) ans += tans;
    	}
    }
    void init(){
    	cnt=0;
    	memset(head, -1, sizeof head);
    }
    int p[N],s[N],ee[N];
    int main(){
    	int t;
    	scanf("%d",&t);
    	for(int cas=1;cas<=t;cas++){
    		printf("Case %d: ",cas);
    		int n,m;
    		int pans=0;
    		init();
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;i++){
    			scanf("%d%d%d",&p[i],&s[i],&ee[i]);
    			add(st,i,p[i]);
    			pans+=p[i];
    			for(int j=s[i];j<=ee[i];j++)
    				add(i,j+n,1);
    		}
    		ed=n+501;
    		for(int i=n+1;i<=n+500;i++){
    			add(i,ed,m);
    		}
    		solve();
    		if(ans==pans)puts("Yes
    ");
    		else puts("No
    ");
    	}
    }
    

    E - Sasha and Array

    CodeForces - 718C 

    给定n((1le nle 10^5))个数(a_i),和m((1le mle 10^5))个操作:

    1 l r x :代表[l,r]区间的a都增加x

    2 l r:代表求(sum_{i=l}^{i=r} fib(a_i)), fib(i) 为第 i 项Fibonacci (斐波那契)数

    对于操作2,输出答案,且对(10^9+7)取模。

    题解1:

    求 fibonacci 时是用矩阵快速幂(矩阵不一定像这样):

    [left[egin{matrix} fib(n-1) & fib(n)\ fib(n) & fib(n+1) end{matrix} ight]= left[egin{matrix} fib(n-2) &fib(n-1) \ fib(n-1) & fib(n) end{matrix} ight] * left[egin{matrix} 0 & 1 \ 1 & 1 end{matrix} ight] ]

    也就是

    [left[egin{matrix} fib(n-1) & fib(n)\ fib(n) & fib(n+1) end{matrix} ight]= left[egin{matrix} 0 & 1 \ 1 & 1 end{matrix} ight]^{n-1} ]

    于是可以用线段树每个节点是矩阵,线段树维护矩阵的和,就可以得到通项和。

    我需要注意的是:

    • 用long long,否则会爆
    • 懒惰标记用法
    • 提前算好bv(b矩阵的x次方)
    • query不要忘记取模
    • 矩阵里的加和乘直接写,而不用for的话,可以-1s。

    题解2:

    考虑斐波那契数列的通项:

    [f[n]=frac{(frac{1+sqrt 5}{2})^n-(frac{1-sqrt 5}{2})^n}{sqrt 5} ]

    用两个线段树维护两个复数 $apm bsqrt 5 $ 的n次幂的区间和:

    1. 增加x,则该区间对应的两个次幂分别乘上x次幂。
    2. 区间求和,即该区间对应的两个次幂相减后的b。因为答案一定是整数,所以相减再除以(sqrt 5) 后,整数部分就是b。

    除以2用乘以inv2 ( 2关于MOD的乘法逆元)来表示。这两个复数就是((inv2,inv2),(inv2,-inv2))

    代码1:

    #include<cstdio>
    #include<cstring>
    #define ll long long
    #define N 100005
    const ll M = 1e9+7;
    using namespace std;
    struct Mat{
    	ll a[2][2];
    	Mat operator + (const Mat &B){
    		Mat C;
    		C.a[0][0]=(a[0][0]+B.a[0][0])%M;
    		C.a[0][1]=(a[0][1]+B.a[0][1])%M;
    		C.a[1][0]=(a[1][0]+B.a[1][0])%M;
    		C.a[1][1]=(a[1][1]+B.a[1][1])%M;
    		return C;
    	}
    	Mat operator * (const Mat &B){
    		Mat C;
    		C.a[0][0]=(a[0][0]*B.a[0][0]+a[0][1]*B.a[1][0])%M;
    		C.a[0][1]=(a[0][0]*B.a[1][0]+a[0][1]*B.a[1][1])%M;
    		C.a[1][0]=(a[1][0]*B.a[0][0]+a[1][1]*B.a[1][0])%M;
    		C.a[1][1]=(a[1][0]*B.a[1][0]+a[1][1]*B.a[1][1])%M;
    		return C;
    	}
    }e={1,0,0,1},b={0,1,1,1},bv,tr[N<<2],lz[N<<2];
    Mat pow (int t){
    	Mat C=e,A=b;
    	while(t){
    		if(t&1)C=C*A;
    		A=A*A;
    		t>>=1;
    	}
    	return C;
    }
    int L[N<<2],R[N<<2];
    void pushDown(int x){
    	tr[x<<1]  =tr[x<<1]  *lz[x];
    	tr[x<<1|1]=tr[x<<1|1]*lz[x];
    	lz[x<<1]  =lz[x<<1]  *lz[x];
    	lz[x<<1|1]=lz[x<<1|1]*lz[x];
    	lz[x]=e;
    }
    void pushUp(int x){
    	tr[x]=tr[x<<1]+tr[x<<1|1];
    }
    void build(int l,int r,int x){
    	L[x]=l;R[x]=r;
    	lz[x]=e;
    	if(l==r){
    		int a;
    		scanf("%d",&a);
    		tr[x]=pow(a-1);
    		return;
    	}
    	int m=l+r>>1;
    	build(l,m,x<<1);
    	build(m+1,r,x<<1|1);
    	pushUp(x);
    }
    void add(int l,int r,int x,int v){
    	if(l<=L[x]&&r>=R[x]){
    		tr[x]=tr[x]*bv;
    		lz[x]=lz[x]*bv;
    		return;
    	}
    	if(L[x]!=R[x])pushDown(x);
    	if(l<=R[x<<1])add(l,r,x<<1,v);
    	if(r>R[x<<1])add(l,r,x<<1|1,v);
    	pushUp(x);
    }
    ll query(int l,int r,int x){
    	if(l>R[x]||r<L[x])return 0;
    	if(l<=L[x]&&r>=R[x])return tr[x].a[1][1];
    	if(L[x]!=R[x])pushDown(x);
    	return (query(l,r,x<<1)+query(l,r,x<<1|1))%M;
    }
    int main() {
    	int n,m,l,r,x,op;
    	scanf("%d %d",&n,&m);
    	build(1,n,1);
    	for(int i=1;i<=m;i++){
    		scanf("%d %d %d",&op, &l, &r);
    		if(op==1) scanf("%d",&x), bv=pow(x), add(l,r,1,x);
    		else printf("%lld
    ",query(l,r,1));
    	}
    	return 0;
    }
    

    代码2:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    const int N=1e5+5;
    const ll M=1e9+7;
    const ll inv2=5e8+4;
    using namespace std;
    
    struct Num{
    	ll a,b;
    	Num():a(1),b(0){}
    	Num(ll x,ll y):a(x%M),b(y%M){}
    	Num operator +(const Num &B)const{
    		return Num(a+B.a,b+B.b);
    	}
    	Num operator *(const Num &B)const{
    		return Num(a*B.a+b*B.b*5,a*B.b+b*B.a);
    	}
    	Num operator ^(int t)const{
    		Num A=(*this),B(1,0);
    		while(t){if(t&1)B=B*A;A=A*A;t>>=1;}
    		return B;
    	}
    }eA(inv2,inv2),eB(inv2,-inv2),tr[2][N<<2],lz[2][N<<2];
    
    void pushup(int i,int x){
    	tr[i][x]=tr[i][x<<1]+tr[i][x<<1|1];
    }
    void pushdown(int i,int x){
    	tr[i][x<<1]=tr[i][x<<1]*lz[i][x];
    	tr[i][x<<1|1]=tr[i][x<<1|1]*lz[i][x];
    	lz[i][x<<1]=lz[i][x<<1]*lz[i][x];
    	lz[i][x<<1|1]=lz[i][x<<1|1]*lz[i][x];
    	lz[i][x].a=1,lz[i][x].b=0;
    }
    
    #define ls l,r,L,L+R>>1,x<<1
    #define rs l,r,(L+R>>1)+1,R,x<<1|1
    
    void add(int l,int r,int L,int R,int x,int i,Num ad){
    	if(l>R||L>r)return;
    	if(l<=L&&R<=r){
    		tr[i][x]=tr[i][x]*ad;
    		lz[i][x]=lz[i][x]*ad;
    		return;
    	}
    	if(L!=R) pushdown(i,x);
    	add(ls,i,ad);add(rs,i,ad);
    	pushup(i,x);
    }
    ll query(int l,int r,int L,int R,int x){
    	if(l>R||L>r)return 0;
    	if(l<=L&&R<=r)return (tr[0][x].b-tr[1][x].b)%M;
    	if(L!=R)pushdown(0,x),pushdown(1,x);
    	return (query(ls)+query(rs))%M;
    }
    
    int main(){
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i=1,a;i<=n;i++){
    		scanf("%d",&a);
    		add(i,i,1,n,1,0,eA^a);
    		add(i,i,1,n,1,1,eB^a);
    	}
    	for(int i=1,t,l,r,x;i<=m;i++){
    		scanf("%d%d%d",&t,&l,&r);
    		if(t==1){
    			scanf("%d",&x);
    			add(l,r,1,n,1,0,eA^x);
    			add(l,r,1,n,1,1,eB^x);
    		}
    		else printf("%lld
    ",query(l,r,1,n,1));
    	}
    	return 0;
    }
    

    F - Lena and Queries

    CodeForces - 678F 

    3种操作:

    1 a b :添加一对数到集合里。

    2 i :移除第 i 次操作添加的一对数。

    3 q : 询问(qcdot x+y)最大值,(x,y)是当前在集合里的。

    给出n((1le nle 3cdot 10^5))次操作,对于3操作输出最大值。

    题解:

    对于每对数,相当于平面坐标的一个点。它出现的操作区间是[L,R],L是添加它的操作编号,如果有移除,则R是移除时的操作下标,否则R是n。

    全部操作读入后,将点插入所在操作区间的线段树节点。

    (z=qcdot x+y)的最大值,相当于过点(x,y)且斜率为-q的直线(y=-qcdot x +z)的y轴截距最大,则(x,y)一定是凸包上点。用单调栈求凸包,再三分求最大值。

    从底向上用线段树维护操作区间里的点形成的凸壳,同时,对在当前区间里的所有3操作,用三分求得在当前区间的凸包点集下,qx+y的最大值,更新一下答案。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #define ll long long
    #define N 300005
    const ll INF = 1LL << 61;
    using namespace std;
    
    int t[N],r[N],empty[N];
    ll ans[N];
    struct Point{
    	ll x,y;
    	bool operator <(const Point &B)const{
    		return x<B.x||(x==B.x&&y<B.y);
    	}
    	Point operator -(const Point &B)const{
    		return (Point){x-B.x,y-B.y};
    	}
    	Point operator +(const Point &B)const{
    		return (Point){x+B.x,y+B.y};
    	}
    	ll operator ^(const Point &B)const{
    		return x*B.y-y*B.x;
    	}
    	ll operator *(const Point &B)const{
    		return x*B.x+y*B.y;
    	}
    }p[N],S[N];
    
    vector<Point> tr[N<<2];
    void insert(int l,int r,int L,int R,int o,int v){
    	if(l<=L&&R<=r){
    		tr[o].push_back(p[v]);
    		return;
    	}
    	int M=L+R>>1;
    	if(l<=M)insert(l,r,L,M,o<<1,v);
    	if(r>M)insert(l,r,M+1,R,o<<1|1,v);
    }
    void query(int x,int n){
    	int l=1,r=n;
    	while(r-l>=3){//三分求最大值
    		int m1=l+(r-l)/3;
    		int m2=l+(r-l)*2/3;
    		if(p[x]*S[m1]<p[x]*S[m2])l=m1;
    		else r=m2;
    	}
    	for(int i=l;i<=r;i++)
    		ans[x]=max(ans[x],p[x]*S[i]);
    }
    
    void solve(int l,int r,int o){
    	if(l<r){
    		int m=l+r>>1;
    		solve(l,m,o<<1);
    		solve(m+1,r,o<<1|1);
    	}
    	sort(tr[o].begin(),tr[o].end());
    	int top=0;
    	for(int i=0;i<tr[o].size();i++){
    		while(top>1&&((S[top]-S[top-1])^(tr[o][i]-S[top]))>=0)top--;
    		S[++top]=tr[o][i];
    	}
    	for(int i=l;i<=r;i++) if(t[i]==3&&!empty[i]) query(i,top);
    }
    
    int main() {
    	int n;
    	scanf("%d",&n);
    	for(int i=1,cnt=0,x;i<=n;i++){
    		scanf("%d",&t[i]);
    		if(t[i]==1)
    			scanf("%lld %lld",&p[i].x,&p[i].y), r[i]=n, cnt++;
    		else if(t[i]==2)
    			scanf("%d",&x), r[x]=i, cnt--;
    		else
    			scanf("%lld",&p[i].x), p[i].y=1LL, empty[i]=(cnt==0), ans[i]=-INF;
    	}
    	
    	for(int i=1;i<=n;i++)if(t[i]==1&&i+1<=r[i]) insert(i,r[i],1,n,1,i);
    
    	solve(1,n,1);
    
    	for(int i=1;i<=n;i++)if(t[i]==3){
    		if(empty[i]) puts("EMPTY SET");
    		else printf("%lld
    ",ans[i]);
    	}
    	return 0;
    }
    

    G - Pouring Rain

    CodeForces - 667A 

    下雨使水上升的速度:(e cm/s)

    喝水使水减少的速度:(v cm^3/s)

    杯子的直径:(d cm)

    杯子里水的初始高度:(h cm)

    求若能喝完水,需要多少秒。

    #include<cstdio>
    #include<cmath>
    #define pi acos(-1.0)
    using namespace std;
    double d,h,v,e;
    int main() {
    	scanf("%lf%lf%lf%lf",&d,&h,&v,&e);
    	double s=v/pi/(d/2)/(d/2)-e;
    	if(s>0)printf("YES
    %f",h/s);
    	else puts("NO");
    	return 0;
    }
    

    H - Constellation

    CodeForces - 618C

    给定n个点,求可组成一个三角形且内部没有其它点的三个点。

    将点按x,再按y排序。选取第一第二个点,再输出不和它们构成直线的第一个点。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #define ll long long
    #define N 100005
    using namespace std;
    struct star{
    	ll x,y;int id;
    }s[N];
    int n;
    int cmp(star a,star b){
    	return a.x<b.x||a.x==b.x&&a.y<b.y;
    }
    int main() {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%lld%lld",&s[i].x,&s[i].y);
    		s[i].id=i;
    	}
    	sort(s+1,s+1+n,cmp);
    	printf("%d %d ",s[1].id,s[2].id);
    		for(int i=3;i<=n;i++){
    			if((s[1].x-s[2].x)*(s[2].y-s[i].y)!=(s[1].y-s[2].y)*(s[2].x-s[i].x)){
    				printf("%d",s[i].id);break;
    			}
    		}
    	return 0;
    }
    
  • 相关阅读:
    c语言学习笔记(6)——for和while循环
    c语言学习笔记(5)——进制
    c语言学习笔记(4)——流程控制
    ckeditor复制粘贴word
    java上传超大文件
    plupload上传整个文件夹
    vue+上传文件夹
    php+上传大文件
    web+文件夹上传
    2G以上的大文件如何上传
  • 原文地址:https://www.cnblogs.com/flipped/p/6358073.html
Copyright © 2011-2022 走看看