Individual Contest #1 and Private Training #1
第一次的增补赛,也是第一场个人排位赛,讲道理打的和屎一样,手速题卡了好久还WA了好多发,难题又切不出来,这种情况是最尴尬的吧!
Individual Contest #1:
Ploblem D:
题意:给你一个含有N个元素的数组和操作次数m,然后求出经过m次操作后,从区间X到Y的总和;操作如下:{1,6,9}==>{1,7,6,15,9}(多CASE)
题目链接:https://www.codechef.com/problems/CHSPARR
思路:
首先,最容易想到的是单纯的模拟每一次这样的操作然后用数组存起来,再去算区间X到Y的总和。可是,当N和m取最大的时候,数组元素数量会达到10^15,直接GG!所以得换个套路!
对于原始数组{1,6,9},元素对应的下标各自为1,2,3。以1,6为例,{1,6}==>{1,7,6}==>{1,8,7,13,6}==>{1,9,8,15,7,20,13,19,6},元素6的标和m的变化为:i(0),2*i-1(1),4*i-3(2),8*i-7(3)
把两者联系起来你可以发现经过m次变换后元素6的下标为:i*2^m-(2^m-1)==>(i-1)*2^m-1。
接着继续模拟:{1,6}==>{1,7(1+6),6}==>{1,8(1,1+6),7(1+6),13(1+6,6),6},你会发现,如果把1和6中间的所有新增加的元素看成一个整体s,那个他每一次都会
向左向右复制一遍,再加上他本身,就是3*s,同时端点处的1和6也会每一次都增加一次,如果能将s和(1+6)联系起来,那么1和6之间的元素总和不就可以直接求了吗!到这里也可以看出
来是倍数关系。设数组k表示分别经过1-m次操作后对应的系数,则k[m]=k[m-1]*3+1。这样的话如果X和Y里面有包含一个或多个完整的中间元素块,就可以直接求出来了!
那多出来的那部分怎么办呢?你会发现对于元素a(i)和元素b(j),元素a+b一定在(i+j)/2的位置。所以直接分治,每一次都缩小规模找到完整块++,代码如下:
#include <iostream> #include <queue> #include <stack> #include <cstdio> #include <vector> #include <map> #include <set> #include <algorithm> #include <cmath> #include <cstring> #include <cstdlib> #include <string> #include <sstream> #define lson l,m,rt*2 #define rson m+1,r,rt*2+1 #define mod 1000000007 using namespace std; typedef long long LL; using namespace std; LL a[100006],k[40],n,m,x,y; void init() { k[0]=0; for(int i=1;i<=30;i++) { k[i]=(k[i-1]*3+1)%mod; } } LL f(LL num) { if(num==0)return 0; int i,j,mm; LL res=0,p=1<<m,q,h,mid,l,r; q=num/p; for(i=1;i<=q;i++) { res=(res+(a[i]+a[i+1])*k[m]+a[i])%mod; } h=num%p; l=a[q+1]; r=a[q+2]; mm=m; while(h>0&&mm>0) { mm--; mid=1<<mm; if(h>=mid) { res=(res+(2*l+r)*k[mm]+l)%mod; h-=mid; l=l+r; } else { r=l+r; } } //if(h!=0)res+=r; return res%mod; } int main() { #ifdef Local freopen("data.txt","r",stdin); #endif int i,j,T; LL ans; cin>>T; init(); while(T--) { cin>>n>>m>>x>>y; for(i=1;i<=n;i++) { scanf("%lld",&a[i]); } ans=f(y)-f(x-1); if(ans<0)ans+=mod; printf("%lld\n",ans); } }
Ploblem H:
题意:给你一棵N节点的树和K种颜色的颜料,对这样的一棵树进行染色,使得任意两个相同颜色的联通节点之间所有节点颜色相同,求出所有的染色方法。
题目链接:https://www.codechef.com/problems/COLTREE
思路 I:
其实就是给你一棵树,让你切分成m棵子树,每棵子树的染色相同,有多少种染色方法。切成m棵子树,其实就是选择m-1条边去切断,所有就是C(n-1,m-1),
然后对于m棵子树进行K种染色,其实就是A(K,m),所有方法数就是C(n-1,m-1)*A(K,m)。然后对于规模不大的大组合数取模,用杨辉三角把除变加进行
处理就可以了。
1 #include <iostream> 2 #include <queue> 3 #include <stack> 4 #include <cstdio> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <cstdlib> 12 #include <string> 13 #include <sstream> 14 #define lson l,m,rt*2 15 #define rson m+1,r,rt*2+1 16 #define mod 1000000007 17 using namespace std; 18 typedef long long LL; 19 LL C[60][60]; 20 LL A(int n,int m) 21 { 22 LL res=1; 23 for(int i=n;i>n-m;i--) 24 { 25 res=(res*i)%mod; 26 } 27 return res; 28 } 29 void init() 30 { 31 memset(C,0,sizeof(C)); 32 for(int i=0;i<=50;i++)C[i][0]=1; 33 for(int i=1;i<=50;i++) 34 { 35 for(int j=1;j<=i;j++) 36 { 37 if(j==1)C[i][j]=i; 38 else if(i==j)C[i][j]=1; 39 else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; 40 } 41 } 42 } 43 int main() 44 { 45 #ifdef Local 46 freopen("data.txt","r",stdin); 47 #endif 48 int n,m,u,v,T,i,j; 49 LL ans; 50 init(); 51 cin>>T; 52 while(T--) 53 { 54 ans=0; 55 cin>>n>>m; 56 for(i=0;i<n-1;i++)scanf("%d%d",&u,&v); 57 for(i=1;i<=min(n,m);i++) 58 { 59 ans=(ans+C[n-1][i-1]*A(m,i))%mod; 60 } 61 cout<<ans<<endl; 62 } 63 }
思路 II:树形DP(等我学会了再更...)
Private Training #1
Problem C (UVALive 6602 Counting Lattice Squares)
题意:给你一个(n+1)*(m+1)的点矩阵,然后让你在点阵里面找出,所有面积为奇数的正方形的个数?
题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4613
思路:
首先,面积为奇数的正方形,可以直接由边长为奇数K得来,也可以由类似这样sqrt(K)的边得来。然后对于第二种情况的K,可以由a+b的和为奇数的情况得来,即
K=sqrt(a*a+b*b)&&(a+b)%2==1。接着你会发现,边长为sqrt(K)的正方形需要的空间是边长为(a+b)的正方形,所以边长为sqrt(K)的情况可以把它当做边长为
(a+b)的一类型,且该区域一共可以画2个不同的边长为sqrt(K)的正方形。然后对于一个(n+1)*(m+1)的点矩阵,一共可以切成(n-i+1)*(m-i+1)种边长为i的正方
形,但是对于边长i,它可以由不同的a+b组合而来,假设i=5,那么5=1+4=2+3,6=1+5=2+4=3+3,所以最后的公式就是(n-i+1)*(m-i+1)*(i/2*2(2种画法)+1(本身))
1 #include <iostream> 2 #include <queue> 3 #include <stack> 4 #include <cstdio> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <cstdlib> 12 #include <string> 13 #include <sstream> 14 #define lson l,m,rt*2 15 #define rson m+1,r,rt*2+1 16 #define mod 1000000007 17 using namespace std; 18 typedef long long LL; 19 int main() 20 { 21 #ifdef Local 22 freopen("data.txt","r",stdin); 23 #endif 24 int i,j; 25 LL ans,s,n,m; 26 while(~scanf("%lld %lld",&n,&m)) 27 { 28 if(!n&&!m)break; 29 ans=0; 30 for(i=1;i<=min(n,m);i+=2) 31 { 32 ans+=((n-i+1)*(m-i+1)*(i/2*2+1)); 33 } 34 cout<<ans<<endl; 35 } 36 37 }
Ploblem J:(UVALive 6609 Minimal Subarray Length)
题意:给你一个含有N个元素的数组和一个价值X,使得数组中连续一段数字和大于等于X的长度最小,如果找不到则输出-1
题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4620
思路:
先用预处理出元素1-i(i<=n)的元素总和sum[i],再用RMQ预处理出dp[i][j]表示区间从i开始,长度为2^j的最大sum,然后再对于每一个i(1-n)二分(query(l,r)-sum[i-1])
>=X,的最短长度,复杂度是n(logn)。
1 #include <iostream> 2 #include <queue> 3 #include <stack> 4 #include <cstdio> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <cstdlib> 12 #include <string> 13 #include <sstream> 14 #define lson l,m,rt*2 15 #define rson m+1,r,rt*2+1 16 #define mod 1000000007 17 using namespace std; 18 typedef long long LL; 19 const int INF=0x3f3f3f3f; 20 LL dp[500005][20],sum[500005]; 21 void RMQ(int n) 22 { 23 int i,j; 24 for(i=1;i<=n;i++)dp[i][0]=sum[i]; 25 for(j=1;(1<<j)<=n;j++) 26 { 27 for(i=1;(i+(1<<j))<=n;i++) 28 { 29 dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); 30 } 31 } 32 } 33 LL query(int l,int r) 34 { 35 int p=0; 36 while(1<<(p+1)<=(r-l+1))p++; 37 return max(dp[l][p],dp[r-(1<<p)+1][p]); 38 } 39 int main() 40 { 41 #ifdef Local 42 freopen("data.txt","r",stdin); 43 #endif 44 int i,j,T,n; 45 LL k; 46 cin>>T; 47 while(T--) 48 { 49 memset(sum,0,sizeof(sum)); 50 cin>>n>>k; 51 for(i=1;i<=n;i++) 52 { 53 scanf("%lld",&sum[i]); 54 sum[i]+=sum[i-1]; 55 } 56 RMQ(n); 57 int ans=INF; 58 for(i=1;i<=n;i++) 59 { 60 int l=i,r=n; 61 while(l<=r) 62 { 63 int m=(l+r)/2; 64 if((query(i,m)-sum[i-1])>=k) 65 { 66 r=m-1; 67 } 68 else l=m+1; 69 } 70 if(r<=n&&sum[r]-sum[i-1]>=k)ans=min(ans,r-i+1); 71 if(l<=n&&sum[l]-sum[i-1]>=k)ans=min(ans,l-i+1); 72 } 73 if(ans==INF)cout<<-1<<endl; 74 else cout<<ans<<endl; 75 } 76 return 0; 77 }
To Be Continue!