zoukankan      html  css  js  c++  java
  • 成电体验营

    最大疯子树

    给定一棵 n 个结点的树,结点编号为 1~n,i 号结点的权重记为 wi(每个点的权值各不相同)。我们定义一个“疯子树”为:

      1. 是一个联通子图。

      2. 我们将子图内的点按照权重从小到大排序后序列为 b1,b2,…,bm,对于任意的 i(i<m),bi到 bi+1最短路径(不含 bi和 bi+1)上的结点的权值都小于等于 i wb 。
    输出包含结点最多的“疯子树”的结点数。

    n≤200000,0 < wi≤100000000

    题解:

    简单的树dp。

    问题转化:

    ​ 我们考虑按照权值从小到大把点加入,如果当前这个点$i$可以加入我们之前的一个合法的联通子图$G$,那么$G$一定和$i$有一条边直接联通。

    ​ 因为$G$里面的点的权值都比i的权值小,所以如果我们把边从无向改为有向,由权值大的点指向权值小的点,那么每个点最多有一个出边,是把这个点加入$G$时和$G$直接相连的那条边,而且后面加入$G$的点如果有向它连的边,那么都是指向它的,是它的入边。

    ​ 那么问题转化为,给你一个由有向边构成的树,让你找到一个最大的联通子图使得每个点的出度不超过1。

    解法:

    ​ 树dp。随便找个点当做根,$dp[i][0/1]$表示$i$这个点向儿子有$0/1$条出边所能形成的最大联通图。

    ​ 如果$x$是i的儿子,那么

    ​ 当$w[x]>w[i]$时,$dp[i][0] += dp[x][0]$ (x连向i)

    ​ 当$w[x]<w[i]$时,$dp[i][1]=max(dp[x][1]) + dp[i][0]$ (i连向x)

    ​ 一定有$dp[i][1]>dp[i][0]$,$ans=max(dp[i][1])$

    ​ 时间复杂度和空间复杂度均为$O(n)$。

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=2e5+7;
    int n,w[maxn],dp[maxn][2],ans;
    
    ll ff;char cc;
    template<typename T> void read(T& aa) {
    	aa=0; ff=1; cc=getchar();
    	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    int fir[maxn],nxt[2*maxn],to[2*maxn],e=0;
    void add(int x,int y) {
    	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
    	to[++e]=x;nxt[e]=fir[y];fir[y]=e;
    }
    
    void dfs(int pos,int f) {
    	int y,z;
    	dp[pos][0]=1; dp[pos][1]=0;
    	for(y=fir[pos];y;y=nxt[y]) {
    		if((z=to[y])==f) continue;
    		dfs(z,pos);
    		if(w[z]>w[pos]) dp[pos][0]+=dp[z][0];//pos <- z
    		else dp[pos][1]=max(dp[pos][1],dp[z][1]);//pos -> z
    	}
    	dp[pos][1]+=dp[pos][0];
    	ans=max(ans,dp[pos][1]);
    }
    
    int main() {
    	freopen("crazy.in","r",stdin);
    	freopen("crazy.out","w",stdout);
    	int x,y;
    	while(scanf("%d",&n)==1) {
    		For(i,1,n) fir[i]=0; ans=0;
    		For(i,1,n) read(w[i]);
    		For(i,1,n-1) {
    			read(x); read(y);
    			add(x,y);
    		}
    		dfs(1,0);
    		printf("%d
    ",ans);
    	}
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    

      

    分解

    给定正整数 N,你需要将其分解为若干正整数的乘积,要求分解出的数之间 相差都不超过 1。

    N≤10^18,T≤10

    题解:

    如果分解成的正整数是x和x+1

    Pollard_rho分解,对于一个质数p_i,如果N中含有a_i个p_i,那么要么x^r包含a_i个p_i,要么(x+1)^t包含,状压枚举x中包含哪些质数

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<map>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int TM=25,maxn=1007;
    const ll INF=2e18;
    ll Td,tot,ans;
    map<ll,ll> G;
    map<ll,ll>::iterator it;
    ll zz[maxn],num[maxn];
    
    ll ff;char cc;
    template<typename T> void read(T& aa) {
    	aa=0; ff=1; cc=getchar();
    	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    ll gcd(ll x,ll y) {
    	return y==0? x:gcd(y,x%y);
    }
    
    ll qc(ll x,ll k,ll mod) {
    	ll rs=0;
    	while(k) {
    		if(k&1) rs=(rs+x)%mod;
    		k>>=1; x=(x+x)%mod;
    	}
    	return rs;
    }
    
    ll qp(ll x,ll k,ll mod) {
    	ll rs=1;
    	while(k) {
    		if(k&1) rs=qc(rs,x,mod);
    		k>>=1; x=qc(x,x,mod);
    	}
    	return rs;
    }
    
    bool isprime(ll n) {
    	if(n==2||n==3||n==5||n==7) return 1;
    	if(n<2||(n%6!=1&&n%6!=5)) return 0;
    	ll m=n-1,t=0,x,y;
    	while((m&1)==0) t++,m>>=1;
    	For(qaq,1,TM) {
    		x=rand()%(n-2)+2;
    		x=qp(x,m,n);
    		For(i,1,t) {
    			y=qc(x,x,n);
    			if(y==1&&x!=1&&x!=n-1) return 0;
    			x=y;
    		}
    		if(y!=1) return 0;
    	}
    	return 1;
    }
    
    ll prho(ll n,ll c) {
    	ll x=rand()*rand()%n+1,y=x,k=2,i=0,d;
    	while(1) {
    		x=(qc(x,x,n)+c)%n+2;
    		d=gcd((y-x+n)%n,n);
    		if(d>1&&d<n) return d;
    		if(x==y) return n;
    		if((++i)==k) {
    			k<<=1; y=x;
    		}
    	}
    	return n;
    }
    
    void find(ll n,ll m) {
    	if(isprime(n)) {
    		++G[n];
    		return;
    	}
    	ll k=m,p=n;
    	while(p>=n&&m) p=prho(n,m--);
    	find(p,k); find(n/p,k);
    }
    
    
    struct Node{
    	ll x,num1,num2;
    	Node(){}
    	Node(ll x,ll num1,ll num2):x(x),num1(num1),num2(num2){}
    	bool operator < (const Node& b) const{return x<b.x;}
    }node[maxn*maxn];
    
    ll p[maxn];
    void get_ans(ll s) {
    	ll gcd1=0,gcd2=0;
    	For(i,1,tot) 
    		if((s>>(i-1))&1){
    			if(!gcd1) gcd1=num[i];
    			else gcd1=gcd(gcd1,num[i]);
    		}
    		else {
    			if(!gcd2) gcd2=num[i];
    			else gcd2=gcd(gcd2,num[i]);
    		}
    	ll x=1,y=1,num1=1,num2=1,o=0;
    	For(i,1,tot) if((s>>(i-1))&1) num1*=qp(zz[i],num[i]/gcd1,INF);
    	For(i,1,tot) if((s>>(i-1)&1)==0) num2*=qp(zz[i],num[i]/gcd2,INF);
    	for(it=G.begin();it!=G.end();++it) {
    		p[++o]=it->first;
    		num[o]=it->second;
    	}
    	For(r,1,gcd1) if(gcd1%r==0) {
    		x=qp(num1,gcd1/r,INF);
    		For(t,1,gcd2) if(gcd2%t==0) {
    			y=qp(num2,gcd2/t,INF);
    			if(y!=x+1) continue;
    			if(y<x) break;
    			node[++ans]=Node(x,r,t);
    		}
    	}
    }
    
    void print_ans(int p) {
    	printf("%lld ",node[p].num1+node[p].num2);
    	For(i,1,node[p].num1) printf("%lld ",node[p].x);
    	For(i,1,node[p].num2) printf("%lld ",node[p].x+1);
    	printf("
    ");
    }
    
    int main() {
    	freopen("little.in","r",stdin);
    	freopen("little.out","w",stdout);
    	ll n,sz,x; read(Td);
    	while(Td--) {
    		read(n);
    		if(n==(n&-n)) {
    			printf("-1
    ");
    			continue;
    		}
    		G.clear(); tot=ans=0;
    		find(n,120);
    		for(it=G.begin();it!=G.end();++it) {
    			zz[++tot]=it->first;
    			num[tot]=it->second;
    		}
    		if(tot<2) {
    			For(i,1,num[1]) if(num[1]%i==0) ++ans;
    			printf("%lld
    ",ans);
    			For(i,1,num[1]) if(num[1]%i==0) {
    				x=qp(zz[1],i,INF);
    				printf("%lld ",num[1]/i);
    				For(j,1,num[1]/i) printf("%lld ",x);
    				printf("
    ");
    			}
    			continue;
    		}
    		sz=1<<tot;
    		ll gcd1=num[1];
    		For(i,1,tot) gcd1=gcd(gcd1,num[i]);
    		For(r,1,gcd1) if(gcd1%r==0) {
    			x=1;
    			For(i,1,tot) x*=qp(zz[i],num[i]/r,INF);
    			node[++ans]=Node(x,r,0);
    		}
    		for(int s=1;s<sz-1;++s) get_ans(s);
    		printf("%lld
    ",ans);
    		sort(node+1,node+ans+1);
    		For(i,1,ans) print_ans(i);
    	}
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    

      

    蛋糕

    今天是鲍勃的生日,爱丽丝打算做一个蛋糕送给他。 这是鲍勃的 n 岁生日,所以爱丽丝的蛋糕必须是正 n 边形。

    而且,鲍勃很喜 欢数字 m,所以这个蛋糕必须放在一个正 m 边形的盒子里。

    为了让气氛更加浪漫, 爱丽丝将在蛋糕的中心插上一根蜡烛,显然,蜡烛既在蛋糕的中心,又在盒子的 中心是最好的。

    换句话说,爱丽丝应该使正 n 边形的蛋糕能被容纳在正 m 边形的盒子里,且 使其中心重合。

    事实上,爱丽丝已经做好了蛋糕,蛋糕是边长为 1 的正 n 边形, 现在她想知道,正 m 边形盒子的最小边长是多少。

    n,m≤1000000000

    题解:

    先在n边形外面套一个lcm(n,m)边形,再在lcm(n,m)边形外面套m边形

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db long double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const db PI=acos(-1);
    db n,m,p;
    
    char cc; ll ff;
    template<typename T>void read(T& aa) {
    	aa=0;cc=getchar();ff=1;
    	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    ll gcd(ll x,ll y) {return y==0? x:gcd(y,x%y);}
    
    int main() {
    	freopen("cake.in","r",stdin);
    	freopen("cake.out","w",stdout);
    	while(scanf("%Lf%Lf",&n,&m)==2) {
    		p=n/gcd(n,m)*m;
    		printf("%.4Lf
    ",cos(PI/p)*tan(PI/m)/sin(PI/n));
    	}
    	return 0;
    }
    

      

    Chika 的烦恼

    Chika 家经营着一家旅馆,她经常要帮家里的忙,有时候,她需要去清理长 满杂草的后院。

    具体地,后院里面的每棵草都有其生长速率(每天所能生长的高 度),在某些给定的日期,她需要把高度大于某个值 b 的杂草割掉一截,使其高 度剩余 b。

    (b 在每次给定的日期时不一定相同。)

    她想请你帮她统计一下,每次她所割掉的草的总量是多少。

    假设最开始每棵草的高度是零。

    1≤n,m≤400000, 1≤ai≤10^6 , 1≤di≤10^12 ,0≤bi≤10^12,d1<d2<...<dm,保证在每次割草时,没有草的高度会超过 2×10^12

    题解:

    由于任何时候草的高度的大小关系一定是和a大小关系是相同的

    所以对草根据a排序,每次被割的草一定是a比较大的一坨,直接在线段树上二分找到这一坨草割掉就可以了

    //Serene
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define db double
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(int i=(a);i>=(b);--i)
    const int maxn=4e5+7;
    ll n,m,a[maxn],b[maxn];
    
    ll ff;char cc;
    template<typename T> void read(T& aa) {
    	aa=0; ff=1; cc=getchar();
    	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    	if(cc=='-') ff=-1,cc=getchar();
    	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    	aa*=ff;
    }
    
    bool cmp(const ll x,const ll y) {
    	return x>y;
    }
    
    ll sum[4*maxn],maxnum[4*maxn],laz[4*maxn],laz2[4*maxn],ql,qr,qx;
    
    int pd(int pos,ll l,ll r) {
    	ll mid=(l+r)>>1;
    	if((laz[pos]==-1&&laz2[pos]==0)||l==r) {
    		laz[pos]=-1; laz2[pos]=0;
    		return mid;
    	}
    	if(laz[pos]!=-1) {
    		laz[pos<<1]=laz[pos<<1|1]=laz[pos];
    		laz2[pos<<1]=laz2[pos<<1|1]=0;
    		sum[pos<<1]=(mid-l+1)*laz[pos];
    		sum[pos<<1|1]=(r-mid)*laz[pos];
    		maxnum[pos<<1]=maxnum[pos<<1|1]=laz[pos];//
    	}
    	if(laz2[pos]) {
    		laz2[pos<<1]+=laz2[pos];
    		laz2[pos<<1|1]+=laz2[pos];
    		sum[pos<<1]+=(b[mid]-b[l-1])*laz2[pos];
    		sum[pos<<1|1]+=(b[r]-b[mid])*laz2[pos];
    		maxnum[pos<<1]+=laz2[pos]*a[l];//
    		maxnum[pos<<1|1]+=laz2[pos]*a[mid+1];//
    	}
    	laz[pos]=-1; laz2[pos]=0;
    	return mid;
    }
    
    void chge(int pos,ll l,ll r) {
    	int mid=pd(pos,l,r);
    	if(l>=ql&&r<=qr) {
    		laz[pos]=maxnum[pos]=qx;
    		laz2[pos]=0;
    		sum[pos]=(r-l+1)*qx;
    		return ;
    	}
    	if(ql<=mid) chge(pos<<1,l,mid);
    	if(qr>mid) chge(pos<<1|1,mid+1,r);
    	sum[pos]=sum[pos<<1]+sum[pos<<1|1];
    	maxnum[pos]=max(maxnum[pos<<1],maxnum[pos<<1|1]);
    }
    
    ll q(int pos,int l,int r) {
    	int mid=pd(pos,l,r);
    	if(l>=ql&&r<=qr) return sum[pos];
    	ll rs=0;
    	if(ql<=mid) rs+=q(pos<<1,l,mid);
    	if(qr>mid) rs+=q(pos<<1|1,mid+1,r);
    	return rs;
    }
    
    ll get_ans(int pos,int l,int r,ll x) {
    	int mid=pd(pos,l,r);
    	if(l==r) return l;
    	if(maxnum[pos<<1|1]>x) return get_ans(pos<<1|1,mid+1,r,x);
    	else return get_ans(pos<<1,l,mid,x);
    }
    
    int ef(ll x) {
    	ql=qr=1; if(q(1,1,n)<=x) return 0;
    	ql=qr=n; if(q(1,1,n)>x) return n;
    	return get_ans(1,1,n,x);
    }
    
    int main() {
    	freopen("grass.in","r",stdin);
    	freopen("grass.out","w",stdout);
    	read(n); read(m);
    	memset(laz,-1,sizeof(laz));
    	For(i,1,n) read(a[i]);
    	sort(a+1,a+n+1,cmp);
    	For(i,1,n) b[i]=b[i-1]+a[i];
    	ll x,y,pos,last=0,tot;
    	For(i,1,m) {
    		read(x); read(y);
    		qx=x-last;
    		pd(1,1,n);
    		sum[1]+=qx*b[n];
    		laz2[1]+=qx; maxnum[1]+=qx*a[1];
    		tot=sum[1];
    		if((pos=ef(y))!=0) {
    			ql=1; qr=pos; qx=y;
    			chge(1,1,n);
    		}
    		printf("%lld
    ",tot-sum[1]);
    		last=x;
    	}
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    /*
    4 4
    1 2 4 3
    1 1
    2 2
    3 0
    4 4
    */
    

      

    总结:

    考场上真想骂自己是个sb。

    t2,Pollard_rho板子写错了总是调不出来,最后写了一个暴力的分解大数。

    t3以前饺学长讲过,还有点印象,记得和什么gcd和lcm有关,但是还是做不出来爆零了。

    弃了t3之后,在最后一个小时才开始做t4。t1和t2对拍的时间都没有。

    考完了觉得自己确实是个sb。

    t4一个这么无聊的线段树,竟然认为先二分再在线段树里面查,两个log能过,结果被卡成60。

    t2自己作死把输出的格式写错了,暴力分都没拿到,成功挂成10分。

    这么容易AK的一套题竟然连暴力分都没拿够。

    主要是这几个问题:

    1、板子不够熟练

    2、数据结构没有用熟

    3、数学和计算几何辣鸡

    4、考场上做题容易受到干扰,注意力不集中

  • 相关阅读:
    POJ 1981 Circle and Points (扫描线)
    UVA 1514 Piece it together (二分图匹配)
    CodeForces 140D New Year Contest
    HDU 4863 Centroid of a Tree
    HDU 4865 Peter's Hobby
    HDU 4870 Rating
    HDU 4864 Task
    CodeForces 766E Mahmoud and a xor trip
    CodeForces 766D Mahmoud and a Dictionary
    CodeForces 767D Cartons of milk
  • 原文地址:https://www.cnblogs.com/Serene-shixinyi/p/8611754.html
Copyright © 2011-2022 走看看