2020年西北工业大学“编程之星”程序设计挑战赛

群里有人发比赛密码就去打了打,题目质量真不错。传送门
两道水题就不贴了。
A 先给数组排序,再求前缀和,然后对于每次询问的x,y保持x<y,
先找到最后一个小于x的下标 l ,那么前l个员工到x的距离最短等于l*x-前缀和;
第一个大于y的下标 r ,那么r之后的员工到y的距离最短用前缀和也可以求,
然后看x和y之间的,当x+y是奇数时(x+y)/2到x最近,当x+y是偶数时(x+y)/2到x和y距离一样近,所以就令小于等于(x+y)/2的走向x,其余的走向y,还是用前缀和计算。

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long ll;
int n,t,a[N],q,x,y;
ll sum[N];
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
    while(q--)
    {
    	scanf("%d%d",&x,&y);
    	if(x>y) swap(x,y);
    	int l=upper_bound(a+1,a+n+1,x)-a-1;
    	ll ans=1ll*l*x-sum[l];
    	int r=upper_bound(a+1,a+n+1,x+y>>1)-a-1;
    	ans+=(sum[r]-sum[l]-1ll*x*(r-l));
    	l=upper_bound(a+1,a+n+1,y)-a-1;
    	ans+=(1ll*(l-r)*y-sum[l]+sum[r]);
    	ans+=(sum[n]-sum[l]-1ll*(n-l)*y);
    	cout<<ans<<endl;
	}
}

F 四等分∠AOB。思路:先找∠AOB 的角平分线OC,再平分∠AOC 和∠BOC,
分成三次平分,二分角即可。(还有一种思路是当角的两条边长度相等时两个端点的中点在角平分线上,延长边不太好写,缩小边还是可以二分,不过边的长度越小时误差会越大可能会wa)。

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const double eps=1e-15;
typedef long long ll;
#define pdd pair<double,double>
int n,t,a[N],q,x,y;
int xa,ya,xo,yo,xb,yb;
double check(double x1,double y1,double x2,double y2)//令点d为(x1,y1),点e为(x2,y2),返回∠dOe的cos值,∠AOB<180,二分之后小于90,cos函数就单调了
{
    double dx1=x1-xo,dy1=y1-yo,dx2=x2-xo,dy2=y2-yo;
    double d1=sqrt(dx1*dx1+dy1*dy1),d2=sqrt(dx2*dx2+dy2*dy2);
    return (dx1*dx2+dy1*dy2)/d1/d2;
}
pdd solve(double x1,double y1,double x2,double y2)//二分
{
    double l1=x1,l2=y1,r1=x2,r2=y2,mid1,mid2;
    for(int i=0;i<=50;i++)
    {
        mid1=(l1+r1)/2;
        mid2=(l2+r2)/2;
        if(check(x1,y1,mid1,mid2)>check(x2,y2,mid1,mid2))
        {
            l1=mid1;
            l2=mid2;
        }
        else r1=mid1,r2=mid2;
    }
    return {l1,l2};
}
int main()
{
    cin>>xa>>ya>>xo>>yo>>xb>>yb;
    pdd res=solve(xa,ya,xb,yb);
    pdd ans1=solve(xa,ya,res.first,res.second);
    pdd ans2=solve(xb,yb,res.first,res.second);    
    printf("%.10lf %.10lf\n",ans1.first,ans1.second);
    printf("%.10lf %.10lf\n",res.first,res.second);
    printf("%.10lf %.10lf",ans2.first,ans2.second);
}

G 首先的想法时先求乘积再分解质因数,但是乘积也太大了,早爆ll了。
然后可以发现每个数的范围是1到9,可以直接统计区间内每个数的个数啊,不会还是不太好写还要分情况,再回过头来看第一句,分解质因数啊,存储的时候就分解呗,最后统计质因数的个数答案不就出来了。

#include<bits/stdc++.h>
using namespace std;
const int N=100010,mod=998244353;
const double eps=1e-15;
typedef long long ll;
#define pii pair<int,int>
int n,t,a[N],m;
struct node
{
	int l,r;
	int num[10];
}tr[N<<2];
void pushup(int rt)
{
	for(int i=1;i<=9;i++)
		tr[rt].num[i]=tr[rt<<1].num[i]+tr[rt<<1|1].num[i];
}
void build(int rt,int l,int r)
{
	if(l==r) 
	{
		tr[rt]={l,l};
		int x=a[l];
		for(int i=2;i<=9;i++)//分解质因数
			while(x%i==0)
			{
				tr[rt].num[i]++;
				x/=i;
			}
	}
	else
	{
		tr[rt]={l,r};
		int mid= l+r>> 1;
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		pushup(rt);
	}
}
void updata(int rt,int l,int r)
{
	if(tr[rt].l==l&&tr[rt].r==l) 
	{
		for(int i=1;i<=9;i++)//先清空再更改
			tr[rt].num[i]=0;
		for(int i=2;i<=9;i++)
			while(r%i==0)
			{
				r/=i;
				tr[rt].num[i]++;
			}
	}
	else 
	{
		int mid =tr[rt].l+tr[rt].r>>1;
		if(l<=mid) updata(rt<<1,l,r);
		else updata(rt<<1|1,l,r);
		pushup(rt);
	}
}
vector<int> query(int rt,int l,int r)
{
	if(l<=tr[rt].l&&tr[rt].r<=r)
	{
		vector<int> res(10);
		for(int i=1;i<=9;i++) res[i]=tr[rt].num[i];
	  return res;
	}
	int mid =tr[rt].l+tr[rt].r>>1;
	vector<int> ans(10,0);
	if(l<=mid) 
	{
		vector<int> res=query(rt<<1,l,r);
		for(int i=2;i<=9;i++) ans[i]+=res[i];
	}
	if(r>mid) 
	{
		vector<int> res=query(rt<<1|1,l,r);
		for(int i=2;i<=9;i++) ans[i]+=res[i]; 
	} 
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",a+i);
	build(1,1,n);
	while(m--)
	{
		int op,l,r;
		scanf("%d%d%d",&op,&l,&r);
		if(op==1)
		{
			updata(1,l,r);
		}
		else 
		{
			vector<int> res=query(1,l,r);
			ll ans=1;
			for(int i=2;i<=9;i++)//1的个数不用统计
				ans=ans*(res[i]+1)%mod;
			printf("%lld\n",ans);
		}
	}
}

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