zoukankan      html  css  js  c++  java
  • 2021“MINIEYE杯”中国大学生算法设计超级联赛7

    2021“MINIEYE杯”中国大学生算法设计超级联赛7

    1003 Fall with Trees

    答案为一个三角的面积加上一堆梯形的面积(其实三角形也可以理解为上底为0的梯形doge),它们的高一定。

    答案为设第x个梯形(包括三角)的下底为f(x)显然有

    $f[1]=0,f[2]=x_{ron}-x_{lson},f[x+1]=f[x]*(2^x-2) $

    然后答案就是 (sum_{i=2}^k y*(f[i]-f[i-1])),发现可以把y和(x_{ron}-x_{lson})提出来

    变成(y*(x_{ron}-x_{lson})sum_{i=2}^k (g[i]-g[i-1]))其中(g[i]=frac{f[i]}{x_{ron}-x_{lson}})

    g[i]显然可以递推。所以这题就解决了?

    其实这个题精度要求很低,g[i]最后会变成一个恒定的数。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define int long long
    #define N 10100
    double pw[N],a[N],sum[N];
    void pre_work(){
    	pw[0]=1;
    	for(int i=1;i<=1000;i++)pw[i]=pw[i-1]*2;
    	a[2]=1;
    	for(int i=3;i<=10000;i++){
    		if(i>1000)a[i]=a[i-1];
    		else a[i]=a[i-1]+a[i-1]/(pw[i-1]-2);
    	}
    	for(int i=2;i<=10000;i++){
    		sum[i]=sum[i-1]+a[i]+a[i-1];
    	}
    }
    signed main(){
    	int T;
    	scanf("%lld",&T);
    	pre_work();
    	while(T--){
    		int k;
    		scanf("%lld",&k); 
    		int x_1,y_1,x_2,y_2,x_3,y_3;
    		scanf("%lld%lld%lld%lld%lld%lld",&x_1,&y_1,&x_2,&y_2,&x_3,&y_3);
    		printf("%.3lf
    ",(y_1-y_2)*(x_3-x_2)*sum[k]/2); 
    	}
    	return 0; 
    } 
    

    表示题意没看懂。

    就是给出2n个篮子其中n个可以取i的倍数个,其他n个可以取0~i个。

    然后让你求拿m个方案数。

    如图是n=3的情况。

    可以将可以取i的倍数个的和可以取0~i个的分成一组,这一组就可以拿随意个且方法唯一。

    (第一个可以取1的倍数的单独一组,最后可以取0~n个的单独一组)。

    结果是有n组可以取随意个,1组可以取0~n个。

    然后枚举最后一组拿的个数i。另外n组的方案就是m-i个物品用n-1个板子隔开(不能有空)的方案。

    答案为(sum_{i=0}^{n}C_{m-i+n-1}^{n-1}=C_{m+n}^{n}-C_{m-1}^{n})

    这个转化?杨辉三角找规律?

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define int long long
    #define N 2010000
    int fac[N],inv[N];
    const int p=1e9+7;
    int ksm(int x,int b){
    	int ans=1;
    	while(b){
    		if(b&1)ans=ans*x%p;
    		b>>=1;
    		x=x*x%p;
    	}
    	return ans;
    }
    void pre_work(){
    	fac[0]=1;
    	for(int i=1;i<=2000000;i++)fac[i]=fac[i-1]*i%p;
    	for(int i=1;i<=2000000;i++)inv[i]=ksm(fac[i],p-2);
    }
    int C(int n,int m){
    	if(n<m)return 0;
    	return fac[n]*inv[m]%p*inv[n-m]%p;
    }
    signed main(){
    	int T;
    	scanf("%lld",&T);
    	pre_work();
    	while(T--){
    		int n,m;
    		scanf("%lld%lld",&n,&m);
    		printf("%lld
    ",(C(n+m,n)-C(m-1,n)+p)%p);
    	}
    	return 0;
    } 
    

    题面太真实了。

    注意到只要不选一开始不选第x个座位(xin{1,2,n-1,n-2})第1和第n个座位早晚会坐上人。而且一定是x两侧最先选的(像极了自己坐角落),然后剩下的问题其实就是在最左空位的左侧和最右空位的右侧都有人了的连续空区间做人的问题。

    然后我打出了表并AC了本题

    设长度为x的这样的区间期望坐下的人数为f(x),显然有(f(x)=f(lfloorfrac{n-1}{2} floor)+f(n-lfloorfrac{n-1}{2} floor-1)+1)(f(1)=f(2)=0)

    对于(xin{1,2,n-1,n-2})情况答案为(f(n-2)+2,f(n-3)+2,f(n-3)+2,f(n-2)+2)

    否则答案为(f(x-2)+f(n-x)+3)。最后统计一下答案就是(2*sum[n-2]+8+(n-4)*3),其中sum是f的前缀和

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define int long long
    #define N 5110000
    const int p=1e9+7;
    int ans[N],inv[N],pw[N],sum[N];
    int ksm(int x,int b){
    	int ans=1;
    	while(b){
    		if(b&1)ans=ans*x%p;
    		b>>=1;
    		x=x*x%p;
    	} 
    	return ans;
    }
    void pre_work(){
    	for(int i=1;i<=1000000;i++){
    		int x=ksm(i,p-2);
    		inv[i]=x;
    	}
    	pw[0]=1;
    	for(int i=1;i<=22;i++)pw[i]=pw[i-1]*2;
    	for(int i=0;i<=20;i++){
    		int begin=pw[i]*2-1;
    		int w=pw[i]-1;
    		int end=pw[i+1]*2-2;
    		for(int j=begin;j<=end;j++)
    			ans[j]=w;
    		if(i==0)continue;
    		for(int j=w-1;j>pw[i-1]-1;j--)ans[begin-w+j]=j;
    	}
    	for(int i=1;i<=1000000;i++)sum[i]=(sum[i-1]+ans[i])%p;
    }
    signed main(){
    	pre_work();
    	int T;
    	scanf("%lld",&T);
    	while(T--){
    		int n;
    		scanf("%lld",&n);
    		if(n==1)printf("1
    ");
    		else if(n==2)printf("1
    ");
    		else if(n==3)printf("666666673
    ");
    		else if(n==4)printf("2
    ");
    		else printf("%lld
    ",((2+ans[n-2]+2+ans[n-3]+sum[n-4])*2+(n-4)*3)%p*inv[n]%p);
    	}
    	return 0;
    } 
    

    一个比较妙的转化:给人和炸弹一个向上的g。一个显然的事是扔炸弹只跟仰角有关,所以问题可以转到平面上。

    人就是方向向上的匀加速直线运动,炸弹是匀速直线运动。t时间之后设人和炸弹到达A点和B点,而且轨迹都是以原点为起点的线段并且线段长度已知。

    考虑被炸死的极限情况人和炸弹的距离正好为r,这种情况下A,B,O构成了一个三角形。用余弦定理可以求得OA、OB的夹角(phi),这其实就是最大的跟竖直方向的夹角。然后考虑怎么根据这个(cosphi)算出概率。

    根据球冠表面积公式可以炸到人的角度对应球冠的表面积为(2pi(v_0t)^2(1-cosphi)),除以总表面积(4pi(v_0t)^2)就是概率。

    然后处理构成不了三角形的情况,这种情况就是必定被炸死和必定炸不死的情况。

    (ps:一个概率为0的事件也可能发生)

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int p=1e9+7;
    int ksm(int x,int b){
    	int ans=1;
    	while(b){
    		if(b&1)ans=ans*x%p;
    		b>>=1;
    		x=x*x%p;
    	}
    	return ans;
    }
    signed main(){
    	int T,t,v,r;
    	scanf("%lld",&T); 
    	while(T--){
    		scanf("%lld%lld%lld",&t,&v,&r);
    		int A=5*t*t;
    		int B=v*t;
    		int C=r; 
    		if(B-A>=C||A-B>=C)printf("1
    ");
    		else if(B+A<=C)printf("0
    ");
    		else {
    			printf("%lld
    ",(1-(1-(A*A%p+B*B%p-C*C%p+p)%p*ksm(2*A*B%p,p-2)%p+p)%p*ksm(2,p-2)%p+p)%p);
    		}
    	}
    	return 0;
    }
    

    把每个点向下一个到达的点连边。模拟转移过程发现一个点最后总会在一个圈里转圈。

    根据对极限的理解g(x)其实跟最后到达的那个环的平均值有关。

    判断所有环的平均值时候相等即可。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define N 101000
    #define int long long
    int c[N],fa[N],a[N],in[N],sum[N],tot[N];
    double w[N];
    void dfs(int u,int x){
    	if(c[u]!=0)return;
    	c[u]=x;
    	dfs(fa[u],x);
    }
    signed main(){
    	int T;
    	scanf("%lld",&T);
    	while(T--){
    		int n;
    		scanf("%lld",&n);
    		for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    		for(int i=1;i<=n;i++)in[i]=0;
    		for(int i=1;i<=n;i++)fa[i]=a[i],in[a[i]]++;
    		queue<int> q;
    		for(int i=1;i<=n;i++)if(in[i]==0)q.push(i);
    		while(!q.empty()){
    			int u=q.front();
    			q.pop();
    			in[fa[u]]--;
    			if(in[fa[u]]==0)q.push(fa[u]); 
    		}
    		int num=0;
    		for(int i=1;i<=n;i++)c[i]=0;
    		for(int i=1;i<=n;i++)
    			if(c[i]==0&&in[i])dfs(i,++num);
    		for(int i=1;i<=num;i++)sum[i]=tot[i]=0;
    		for(int i=1;i<=n;i++)
    			if(c[i])sum[c[i]]+=a[i],tot[c[i]]++;
    		for(int i=1;i<=num;i++)w[i]=(double)sum[i]/tot[i];
    		bool flag=0;
    		for(int i=2;i<=num;i++)
    			if(w[i]!=w[1]){
    				printf("NO
    ");
    				flag=1;
    				break;
    			}
    		if(flag==1)continue;
    		printf("YES
    ");
    	}
    	return 0;
    }
    

    1010 Smzzl with Tropical Taste

    签到

    1012 Yiwen with Sqc

    对于每个字符单独考虑。考虑x时,求出x出现的位置a[i],特别地a[0]=0,a[cnt+1]=n+1。

    然后处理出以第i个字母为最开始出现字母区间左端点的可能选择数量l[i]和以第i个字母为最后出现字母区间右端点的可能选择数量r[i],发现r[i]=l[i+1],设b[i]=l[i],b[i]=a[i]-a[i-1]。

    然后答案就是(sum_{len=1}^{cnt}len^2sum_{i=1}^{cnt-len}b[i]*b[i+len])

    好然后上卷积,T了

    考虑O(n)处理这个东西。

    对于b[i],考虑它跟(b[j](j<i))构成的贡献=(b[i]*sum_{j=1}^{i-1}b[j]*(i-j)^2),总答案就是(sum_{i=2}^{n}b[i]*sum_{j=1}^{i-1}b[j]*(i-j)^2)

    考虑维护(sum_{j=1}^{i-1}b[j]*(i-j)^2),发现b[j]的系数为1,4,9,16,25.....(n^2),变化的值就是(b[i]+sum_{j=1}^{i-1}b[j]*[1+2*(i-j)]),这个变化的值变化的值(doge)就是(b[i]+sum_{j=1}^{i-1}b[j]*2)

    求出这个变化值的变化值,进而求出变化值,进而求出答案。

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int p=998244353;
    const int N=501000;
    int a[N];
    int n,m;
    char s[N];
    int sum[N],num[N],cnt[N];
    int D_a[N],S[N];
    vector<int> w[50];
    signed main(){
        int T;
        scanf("%lld",&T);
        while(T--){
            scanf("%s",s + 1);
            n = strlen(s + 1);
            for(int i = 1;i <= 26;++i) w[i].clear(),w[i].push_back(0),cnt[i]=1;
            for(int i = 1;i <= n;++i){
                int x = s[i] - 'a' + 1;
                w[x].push_back(i);
                cnt[x]++;
            }
            for(int i=1;i<=26;i++)w[i].push_back(n+1);
            int ans=0;
            for(int k=1;k<=26;k++){
                n=m=cnt[k]-1;
                if(n<=0)continue;
                for(int i=0;i<=n;i++)a[i]=w[k][i+1]-w[k][i];
                for(int i=0;i<=n;i++)
                    if(i==0)D_a[i]=2*a[i]%p;
                    else D_a[i]=(D_a[i-1]+a[i]*2)%p;
                for(int i=0;i<=n;i++){
                    if(i==0)S[i]=a[i];
                    else S[i]=(S[i-1]+D_a[i-1]+a[i])%p;
                }
                int tmp=0;
                for(int i=0;i<=n;i++){
                    ans=(ans+tmp*a[i]%p)%p;
                    tmp=(tmp+S[i])%p;
                }
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    UNIX网络编程读书笔记:辅助数据
    使用Android Studio搭建Android集成开发环境(图文教程)
    Android数据存储(二)----PreferenceFragment详解
    Android系列----JUnit单元测试的使用
    Android数据存储(一)----SharedPreferences详解
    使用IntelliJ IDEA 13搭建Android集成开发环境(图文教程)
    Android系列之网络(三)----使用HttpClient发送HTTP请求(分别通过GET和POST方法发送数据)
    Android系列之网络(二)----HTTP请求头与响应头
    Android系列之网络(一)----使用HttpClient发送HTTP请求(通过get方法获取数据)
    Android多线程----异步消息处理机制之Handler详解
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/15130247.html
Copyright © 2011-2022 走看看