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[i−1],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} 2m−k(m为有用点的总数,k为连通块个数)。
-
证明大致为:将区间转成两个点,即可判断哪些区间是重复的,如此即可判断有多少个连通块。对于一个大小为n的连通块,贡献为 2 n − 1 2^{n-1} 2n−1,将贡献相乘即为答案。
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)); } }
总结
其实今天比赛红红火火恍恍惚惚,但是策略在昨天写了自我剖析之后有改进,继续加油!