【比赛】2019暑假排位(7)

本文详细解析了2015年ACM-ICPC亚洲达卡区域赛的题目,包括签到题、最大集合问题、中位数矩阵问题、中位数求解及阿基米德螺旋线面积计算。提供了完整的代码实现和算法思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

比赛

The 2015 ACM-ICPC Asia Dhaka Regional Contest

ABCDEFGHIJ
ZY**YYL**Z**L**L**Y**Z

题解

A

签到题,扫一遍统计一下

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>

using namespace std;
int T, n;
int cnt[12][3][3];
char s[5];
int main()
{
    int d, l;
    cin>>T;
    while(T--){
        long long ans = 0;
        memset(cnt, 0, sizeof(cnt));
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i){
            scanf("%d%d%s", &d, &l, s);
            cnt[d][l][s[0] == 'c']++;
        }
        for (int i = 2; i <= 10; ++i){
            cnt[i][0][0] += cnt[i-1][0][0];
        }
        for (int i = 2; i <= 10; ++i)
            ans += 1ll * cnt[i][1][1] * cnt[i - 1][0][0];
        printf("%d\n", ans);
    }

}

B

C

D

E

求一个集合,使得该集合所有元素的最小公倍数为 n n n,最大化集合中所有元素的和,有 T T T组数据。
答案明显是 1 1 1 n n n所有数约数和的和。

  1. 直接埃氏筛,就可以过,复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
  2. 直接线筛,复杂度 O ( n ) O(n) O(n)
  3. 推导一下 a n s = ∑ i = 2 n ∑ d ∣ i d = ∑ d = 1 n d ⌊ n d ⌋ ans=\sum_{i=2}^{n}\sum_{d|i}d=\sum_{d=1}^{n}d\lfloor\frac{n}{d}\rfloor ans=i=2ndid=d=1nddn
    所以直接整数分块就可以了,复杂度 O ( T n ) O(T\sqrt n) O(Tn )
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define inl inline
#define re register
#define MAXN 20000000
using namespace std;
LL t[20000101];
LL ans[MAXN+10];
int n;
int main()
{
	
	for(int i=1;i<=MAXN;i++)
		for(int j=i;j<=MAXN;j+=i)t[j]+=i;
	for(int i=1;i<=MAXN;i++)
		ans[i]=ans[i-1]+t[i];
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0)return 0;
		printf("%lld\n",ans[n]-1);
	}
	return 0;
}

F

题意:求中位数大于h的最大矩形。
把大于等于h的数作为1, 小于h的数作为-1,问题转换为求一个最大的大于等于0的矩阵,很容易想到一个带log的做法,然后就会被出题人卡掉。所以有一个很妙的尺取法,求完前缀和后,考虑对每一个值,应该尽量的取左面的合法的值。所以先扫一遍求一个递减的序列,然后从右向左枚举每一个值,在数组中维护一个右端点,然后尽量取左面的点就可以了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 300;
int R, C;
int q[N], a[N][N], pic[N][N];
int sum[N][N], pre[N];
inline void solve(){
    memset(sum, 0, sizeof(sum));
    for (int i = 1; i <= R; ++i){
        for (int j = 1; j <= C; ++j)
            sum[i][j] = pic[i][j] + sum[i-1][j];
    }
}
inline int getans(int up, int down){
    for (int i = 1; i <= C; ++i)
        pre[i] = pre[i-1] + sum[down][i] - sum[up - 1][i];
    int r = 0;
    for (int i = 1; i <= C; ++i)
        if (pre[i] < pre[q[r]]) q[++r] = i;
    int ans = 0;
    for (int i = C; i; i--){
        while(r && pre[q[r - 1]] <= pre[i]) r--;
        if (pre[q[r]] <= pre[i]) ans = max(ans, (down - up + 1) * (i - q[r]));
    }
    return ans;
}
int main()
{
    int h, q, T, ans;
    cin>>T;
    for (int p = 1; p <= T; ++p){
        printf("Case %d:\n", p);
        scanf("%d%d", &R, &C);
        for (int i = 1; i <= R; ++i)
            for (int j = 1; j <= C; ++j)
                scanf("%d", &a[i][j]);
        scanf("%d", &q);
        for (int i = 1; i <= q; ++i){
            ans = 0;
            scanf("%d", &h);
            for (int j = 1; j <= R; ++j)
                for (int k = 1; k <= C; ++k)
                    pic[j][k] = a[j][k] >= h ? 1 : -1;
            solve();
            for (int up = 1; up <= R; ++up)
                for (int dn = up; dn <= R; ++dn)
                    ans = max(ans, getans(up, dn));
            printf("%d\n", ans);
        }
    }
}

G

H

本质上是求中位数。
前置,已知一维数轴点集 { a n } \{a_n\} {an},求使得曼哈顿距离之和的最小的点的位置:

  1. 如果 n n n是奇数,那么应该取 a n 2 a_{\frac n2} a2n
  2. 如果 n n n是偶数,那么取 [ a n 2 , a n 2 + 1 ] [a_{\frac n2}, a_{\frac{n}{2}+1}] [a2n,a2n+1]中的任何一个数,得到的曼哈顿距离之和相等且最小

如果在其他高维坐标下求解点的位置,由于每一维坐标互不干扰,直接分别计算即可。
显然在本题,当 n n n是奇数的时候,答案肯定为 0 0 0,当 n n n是偶数的时候,稍微推一下式子然后发现是可以卷积的,直接 N T T NTT NTT即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define inl inline
#define re register
#define MAXN 101000
using namespace std;
const LL mod=7340033;
LL poww(LL i,LL k)
{
	if(k<0||i==0)return 0;
	LL ans=1;
	for(;k;k>>=1,i=(i*i)%mod)if(k&1)ans=(ans*i)%mod;
	return ans;
}
LL invv(LL i){return poww(i,mod-2);}
LL fac[MAXN];
LL C(int m,int n){return m<=n?(fac[n]*invv((fac[m]*fac[n-m])%mod))%mod:0;}
LL R[MAXN<<2],BIT,ml,g=3;
int lsn;
void ntt(LL *a,int n,int inv)
{
	if(lsn!=n&&(lsn=n))
		for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(BIT-1));
	for(int i=0;i<n;i++)if(i<R[i])swap(a[i],a[R[i]]);
	for(int i=1;i<n;i<<=1)
	{
		LL mi=(inv==1)?poww(g,(mod-1)/(i<<1)):invv(poww(g,(mod-1)/(i<<1)));
		for(int j=0;j<n;j+=(i<<1))
		{
			LL x=1;
			for(int k=0;k<i;k++,x=(x*mi)%mod)
			{
				LL t1=a[j+k],t2=(x*a[j+k+i])%mod;
				a[j+k]=(t1+t2)%mod,a[j+k+i]=((t1-t2)%mod+mod)%mod;
			}
		}
	}
	if(inv==-1)for(int i=0;i<n;i++)a[i]=(a[i]*invv(n))%mod;
}
int a,b,c,d,n;
LL A[MAXN<<2],B[MAXN<<2];
LL ans[MAXN];
int main()
{
	LL x,y;
	int T;cin>>T;
	fac[0]=1;
	for(int i=1;i<=MAXN-11;i++)fac[i]=fac[i-1]*i%mod;
	for(int u=1;u<=T;u++)
	{
		scanf("%d",&n);
		for(ml=1,BIT=0;ml<=2*n;ml<<=1,BIT++);
		for(int i=0;i<=ml;i++)A[i]=B[i]=0;
		a=b=c=d=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld%lld",&x,&y);
			if(x>0&&y>0)a++;
			if(x<0&&y>0)b++;
			if(x<0&&y<0)c++;
			if(x>0&&y<0)d++;
		}
		for(int i=0;i<=n;i++)A[i]=C(i,a)*C(i,c)%mod;
		for(int i=0;i<=n;i++)B[i]=C(i,b)*C(i,d)%mod;
		ntt(A,ml,1),ntt(B,ml,1);
		for(int i=0;i<ml;i++)A[i]=A[i]*B[i]%mod;
		ntt(A,ml,-1);
		printf("Case %d:\n",u);
		for(int i=1;i<=n;i++)
		{
			if(i&1)ans[i]=0;
			else ans[i]=A[i/2];
		}
		for(int i=1;i<=n;i++)
		{
			printf("%lld%c",ans[i],(i==n)?'\n':' ');
		}
	}
	return 0;
}
*/

I

J

统计阿基米德螺旋线中几块的面积,一开始积分积错了,推出式子之后细节写挂了,几个细节就是考虑从第n条线到第1条线的面积,还有第一圈里面的面积,这两种情况要考虑一下,改了好久

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
double ar;
const double pi=acos(-1);
double ccal(double th1,double th2,int T)
{
    th1 = max(th1 + 2 * pi * T, 0.), th2 = max(th2 + 2 * pi * T, 0.);
    return ar * ar * (th2*th2*th2 - th1*th1*th1)/6;
}
double cal(double th1,double th2,int T)
{
    return fabs(ccal(th1,th2,T)-ccal(th1,th2,T-1));
}
double th[20];
struct shape{
    double th1, th2;
    int T;
}ts[1010];
inline int cmp(const shape &a, const shape &b){return a.th1 == b.th1? a.T < b.T : a.th1 < b.th1;}
int main()
{
    int n, m, T;
    cin>>T;
    for (int k = 1; k <= T; ++k){
        double pt, pr, th1, th2, ans = 0;
        scanf("%lf", &ar);
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) scanf("%lf", &th[i]);
        sort(th + 1, th + n + 1);
        th[n + 1] = th[1] + 2 * pi;
        scanf("%d", &m);
        for (int i = 1; i <= m; ++i){
            scanf("%lf%lf", &pr, &pt);
            if (pt < th[1]) th1 = th[n], th2 = th[n+1];
            else for (int j = 1; j <= n; ++j) {
                if (pt > th[j] && pt < th[j + 1]) {
                    th1 = th[j], th2 = th[j + 1];
                    break;
                }
            }
            int l = 0, r = 1000000, res = 0;
            while(l <= r){
                int mid = (l + r) >> 1;
                if (ar * (2 * pi * mid + pt) > pr) res = mid, r = mid - 1;
                else l = mid + 1;
            }
            if (th1 == th[n] && pt < th[1]) ts[i] = (shape){th1, th2, res - 1};
            else ts[i] = (shape){th1, th2, res};
        }
        sort(ts + 1, ts + m + 1, cmp);
        for (int j = 1; j <= m; ++j){
            if (ts[j].th1 == ts[j-1].th1 && ts[j].T == ts[j-1].T) continue;
            else ans += cal(ts[j].th1, ts[j].th2, ts[j].T);
        }
        ans = ans / 10;
        if (n == 0) printf("Spiral %d: -1\n", k);
        else printf("Spiral %d: %.4f liters\n", k, ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值