2019.6.25总结

2019.6.25总结

比赛情况

100+30+4=134

Rank 1(久违…


T1

  • 其实这道题真的没有那么难(好奇为什么那么多人没有想到…)

  • 首先很容易想到把问题转化成求a序列前面有多少个序列(最后再+1);那么对于一个处于序列第i位的数a[i],我们将其-1,则可以得到前面有多少个数。

  • 那么我们维护一个s数组,其间的s[ i ][ j ]表示目前到了第i位,前面最大的值是j时后面的贡献;显然,这个s的i位只与位置有关,所以可以变枚举边更新s,省略一维。

  • 我们倒着来做,发现每次的s数组,都可以由上一次的s更新过来。就是 s [ i ] = s [ i ] ∗ i + s [ i + 1 ] s[i]=s[i]*i+s[i+1] s[i]=s[i]i+s[i+1] (因为每次前面可以多填 [ 1 , i ] [1,i] [1,i]的数,或者 i + 1 i+1 i+1(但需要特殊处理,即 s [ i + 1 ] s[i+1] s[i+1]))。

  • 统计答案时就直接用 s [ m a x ( m a [ i − 1 ] , a [ i ] − 1 ) ] ∗ ( a [ i ] − 1 ) s[max(ma[i-1],a[i]-1)]*(a[i]-1) s[max(ma[i1],a[i]1)](a[i]1)​累加进答案即可。

    code

  • #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define N 20005
    #define mo 1000007
    #define ll long long
    using namespace std;
    //For 2000 yuan
    ll n,a[N],ans,ma[N],s[N];
    int main()
    {
    	scanf("%lld",&n);
    	for (ll i=1;i<=n;i++)
    		scanf("%lld",&a[i]),ma[i]=max(ma[i-1],a[i]),s[i]=1;
    	s[n+1]=1;
    	for (ll i=n;i;i--)
    	{
    		(ans+=(s[max(ma[i-1],a[i]-1)]*(a[i]-1))%mo)%=mo;
    		for (int j=1;j<=n;j++)
    			s[j]=(j*s[j]%mo+s[j+1])%mo;	
    	}
    	printf("%lld",(ans+1)%mo);
    }
    
    

T2

  • 考试的时候打炸了…

  • 但是事实上思路清奇,把区间转化成图,也是好方法…

  • 对于一个区间,将其转化成[L,R+1),然后将L这个点和R+1这个点合并,置于同个连通块中,最后输出 2 m − k 2^{m-k} 2mk(m为有用点的总数,k为连通块个数)。

  • 证明大致为:将区间转成两个点,即可判断哪些区间是重复的,如此即可判断有多少个连通块。对于一个大小为n的连通块,贡献为 2 n − 1 2^{n-1} 2n1,将贡献相乘即为答案。

    code

  • #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define N 300005
    #define mo 1000000007
    #define ll long long
    #define mem(x) memset(x,0,sizeof x)
    using namespace std;
    //For 2000 yuan
    int T,n,m,tot,g,f[10000005],ans,size[10000005];
    struct node{ll l,r;}a[N];
    int get(int x){return (f[x]==x)?x:f[x]=get(f[x]);}
    int main()
    {
    	scanf("%d",&T);
    	while (T--)
    	{
    		mem(f),mem(a),scanf("%d%d",&n,&m),tot=g=0;
    		for (int i=1;i<=m;i++)
                scanf("%d%d",&a[i].l,&a[i].r),a[i].r++,f[a[i].l]=a[i].l,f[a[i].r]=a[i].r,size[a[i].l]=size[a[i].r]=1;
    		for (int i=1,x,y;i<=m;i++)
    		{
    			x=get(a[i].l),y=get(a[i].r);
    			if (x!=y)
    				(size[x]>size[y])?(f[y]=x,size[x]+=size[y]):(f[x]=y,size[y]+=size[x]);
    		}
    		n=0,ans=1;
    		for (int i=1;i<=m;i++)
    		{
    			f[a[i].l]=get(a[i].l);
    			if (a[i].l==f[a[i].l])
    				n+=size[a[i].l]-1,size[a[i].l]=1;
    			f[a[i].r]=get(a[i].r);
    			if (a[i].r==f[a[i].r])
    				n+=size[a[i].r]-1,size[a[i].r]=1;
    		}
    		for (int i=1;i<=n;i++)
    			ans=ans*2%mo;
    		printf("%d\n",ans);
    	}
    }
    
    

T3

  • 考试自信满满,可以拿68。

  • 但是结果却是数组开小了…QwQ

  • 很暴力…直接打暴力就好了,这里不做赘述。

  • 直接上代码。

    code

  • #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define N 3000005
    #define ll long long
    using namespace std;
    //For 2000 yuan
    ll w,h,n,sum[N],p[N],q;
    ll dis(int x1,int y1,int x2,int y2){return max(abs(x1-x2),abs(y1-y2));}
    int E(int x,int y){return max(0ll,(x-1)*h+y);}
    int main()
    {
    	scanf("%lld%lld",&w,&h),scanf("%lld",&n);
    	for (int t=1,x,y,a,b;t<=n;t++)
    	{
    		scanf("%d%d%d%d",&x,&y,&a,&b);
    		for (int i=1;i<=w;i++)
    			for (int j=1;j<=h;j++)
    				p[E(i,j)]+=max(0ll,(ll)(a-b*dis(x,y,i,j)));
    	}
    	for (int i=1;i<=w;i++)
    		for (int j=1;j<=h;j++)
    			sum[E(i,j)]=sum[E(i-1,j)]+sum[E(i,j-1)]-sum[E(i-1,j-1)]+p[E(i,j)];
    	scanf("%lld",&q);
    	for (int i=1,x1,y1,x2,y2;i<=q;i++)
    	{
    		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    		printf("%lld\n",(ll)(1.0*(sum[E(x2,y2)]-sum[E(x2,y1-1)]-sum[E(x1-1,y2)]+sum[E(x1-1,y1-1)])/((x2-x1+1)*(y2-y1+1))+0.5));
    	}
    }
    

总结

其实今天比赛红红火火恍恍惚惚,但是策略在昨天写了自我剖析之后有改进,继续加油!

(中考加油!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值