第五次ACM训练(Wednesday)

总结

第五次训练成绩感觉不错,第一次拿了rank1,在这里小小的表扬一下自己。但自己也暴露了以下问题:1、在小数浮点边界处理方面经验不足,容易犯浑 2、知识广度储备还不够

A - Character Encoding (容斥+组合数学)

description

m个数,每个数取[0…n-1],问总和为k的方案数(n,m,k<=1e5,T<=400,sum m<=5e6)

solution

若不考虑每个数的范围限制,很容易知道方案就是Ck+m1m1C_{k+m-1}^{m-1},但现在有了范围限制,我们考虑容斥一下枚举>=n的数量变成i=0min(m,k/n)(1)iCmiCkni+m1m1\sum_{i=0}^{min(m,k/n)}{(-1)^{i}*C_{m}^{i}*C_{k-n*i+m-1}^{m-1}}单次询问O(min(m,k/n))由于所有m加起来<5e6,所以过了
题外话,我之所以会算出这条式子后大眼瞪小眼想要给它降低复杂度10min,是因为没看到sum m<=5e6这个条件23333

code

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std; 
const ll maxn=2e5+5;
const ll mo=998244353;
ll c[maxn],ni[maxn];
ll n,m,i,t,j,k,l,x,y,z,p,T,r,mid,q;
ll ans,ans1;
ll mi(ll x,ll y){
	if (y==1) return x;
	ll t=mi(x,y/2);
	if (y%2) return t*t%mo*x%mo;return t*t%mo;
}
ll dg(ll x,ll y){
	return c[x]*ni[y]%mo*ni[x-y]%mo;
}
int main(){
	c[0]=1;
	fo(i,1,maxn-5)c[i]=c[i-1]*i%mo;
	t=maxn-5;ni[t]=mi(c[t],mo-2);
	rp(i,t-1,0) ni[i]=ni[i+1]*(i+1)%mo;
	scanf("%lld",&T);
	while (T--){
		scanf("%lld%lld%lld",&n,&m,&k);
		if ((ll)(n - 1) * m < k) {
			printf("0\n");
			continue;
		}
		ans=0;t=k/n;p=-1;
		fo(i,0,t){
			p=-p;
			ans+=p*dg(m,i)*dg(k-n*i-1+m,m-1)%mo;
		}
		ans=(ans%mo+mo)%mo;
		printf("%lld\n",ans);
	} 
}

B - Pizza Hub (计算几何)

description

把一个三角形放到一个指定宽度的矩形内,问矩形的最小高度

solution

历史的经验教训表明,实数在0附近要控制精度如给一个1e-10的范围
我们发现最小的情况一定可以转化为是三角形的一个顶点钉在矩形的一个顶点,另一个顶点钉在矩形的对边(包含顶点),然后枚举顶点讨论下图这两种情况即可
在这里插入图片描述

code

#include<bits/stdc++.h>
#define ll long long
#define db double
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std; 
const ll maxn=2e5+5;
struct code{
	db x,y;
}a[3];
int n,m,i,t,j,k,T;
db l,r,mid,x,y,z,w,b,c,d,p,s,mx,mi;
db sqr(db x){return x*x;}
db dg(int x,int y){return sqrt(sqr(a[x].x-a[y].x)+sqr(a[x].y-a[y].y));}
db dg1(int x,int y){return sqr(a[x].x-a[y].x)+sqr(a[x].y-a[y].y);}
db pan(int x,int y,int z){
	db x2=a[y].x-a[x].x,x3=a[z].x-a[x].x,y2=a[y].y-a[x].y,y3=a[z].y-a[x].y;
	db c1=(y2*y3+x2*x3)/sqrt(dg1(x,y)*dg1(x,z)),c2=w/dg(x,y);
	db s1=sqrt(1-sqr(c1)),s2;
	if (c2>1) c2=1,s2=0;
	else s2=sqrt(1-sqr(c2));
	db c3=c1*c2-s1*s2,s3=c1*s2+c2*s1,t=dg(x,z);
	db first,second;
	if (c3*t>w+1e-10 || c3*t<-1e-10)first=1e9;
	else first=max(s3*t,s2*dg(x,y));
	c3=c2*c1+s2*s1;s3=s2*c1-c2*s1;
	if (s3<-1e-10 || c3*t>w+1e-10) second=1e9;
	else second=s2*dg(x,y);
	return min(first,second);
}
int main(){
	scanf("%d",&T);
	while (T--){
		scanf("%lf%lf%lf%lf%lf%lf%lf",&a[0].x,&a[0].y,&a[1].x,&a[1].y,&a[2].x,&a[2].y,&w);
		l=min(pan(0,1,2),pan(0,2,1));
		l=min(l,min(pan(1,0,2),pan(1,2,0)));
		l=min(l,min(pan(2,0,1),pan(2,1,0)));
		l+=1e-12;
		if (l<1e9)printf("%.9lf\n",l);
		else printf("impossible\n");
	}
}

J - Taotao Picks Apples (二分)

description

一个长度为n序列,m个询问,每次修改1个点的高度(可逆),问修改后的从头开始取每次取比当前高度更高的数的个数的答案

solution

我们讨论两种情况:1、修改的数本来不在答案序列里的数,若降低则对答案没影响,若升高则看看它离他最近的左右两个数,答案更新一下
2、修改的数本来在答案序列里,则同样讨论一下就好啦

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int t,n,m,p,q,num=0,num2=0;
int h[maxn],maxh[maxn],ans[maxn],b[maxn],maxh2[maxn],o[maxn];
int main(){
	cin>>t;
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) scanf("%d",&h[i]);
		num=0;num2=0;
		for(int i=0;i<=n+5;i++) {
			o[i]=0;
			maxh[i]=0;
			maxh2[i]=0;
			ans[i]=0;
		}
		for(int i=1;i<=n;i++){
			if(h[i]>maxh[i-1]){
				maxh[i]=h[i];
				ans[i]=ans[i-1]+1;
				b[++num]=h[i];
				maxh2[++num2]=0;
				o[num]=num2;
			}
			else{
				maxh[i]=maxh[i-1];
				ans[i]=ans[i-1];
				if(h[i]>maxh2[num2])	maxh2[++num2]=h[i];
			}
		}
		o[num+1]=num2+1;
	/*	for(int i=1;i<=num2;i++) printf("%d ",maxh2[i]);
		printf("\n");
		for(int i=1;i<=num;i++) printf("%d ",b[i],o[i]);
		printf("\n");*/
		while(m--){
			scanf("%d%d",&p,&q);
			if(h[p]==q){
				printf("%d\n",ans[n]);
			}
			if(q<h[p]){
				if(ans[p]==ans[p-1]) printf("%d\n",ans[n]);
				else{
					int sum=0;
					if(q>maxh[p-1]) sum=ans[p-1]+1;
					else sum=ans[p-1];
					int tmp=upper_bound(b+1,b+num+1,h[p])-b;
				//	cout<<tmp<<" ";
					int tmp2=upper_bound(maxh2+o[tmp-1],maxh2+o[tmp],max(q,maxh[p-1]))-maxh2;
					sum+=num-tmp+1+o[tmp]-tmp2;
					printf("%d\n",sum); 
				}
			}
			if(q>h[p]){
				if(q>maxh[p-1]){
					int tmp=upper_bound(b+1,b+num+1,q)-b;
				//	cout<<tmp<<" ";
					printf("%d\n",ans[p-1]+1+num-tmp+1);
				}
				else printf("%d\n",ans[n]);
			}
		}
	}
	return 0;
} 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章