1001 OO’s Sequence
定义两个数组L[i],R[i]保存第i个数左右最近的因子。
那么第i个数对ans的贡献就是(R[i]-i)*(i-L[i])。
更新L,R数组的方法如下:
用向量预存[1,10000]中每个数的位置。
对于a[i],枚举它的所有倍数,记为j。
若j在a[i]左边,且a[i]小于原来的R[j],即a[i]与j更近,那么R[j]的位置更新为i。
若j在a[i]右边,且a[i]大于原来的L[j],即a[i]与j更近,那么L[j]的位置更新为i。
【标程做了一个优化,将小于100的因子先处理。】
1 # include <iostream> 2 # include <cstdio> 3 # include <algorithm> 4 # include <vector> 5 using namespace std; 6 typedef long long LL; 7 # define MOD 1000000007 8 # define maxn 100010 9 vector <int> vec[10001]; 10 int a[maxn],L[maxn],R[maxn]; 11 12 int main(void) 13 { 14 int n; 15 while((scanf("%d",&n))!=EOF) 16 { 17 for(int i=1;i<=10000;i++) vec[i].clear(); 18 for(int i=1;i<=n;i++) 19 { 20 scanf("%d",a+i); 21 L[i]=0; R[i]=n+1; 22 vec[a[i]].push_back(i); 23 } 24 for(int i=1;i<=n;i++) 25 for(int j=a[i];j<=10000;j+=a[i]) 26 for(int k=0;k<vec[j].size();k++) 27 { 28 if(vec[j][k]>=i) break; 29 else R[vec[j][k]]=min(R[vec[j][k]],i); 30 } 31 for(int i=1;i<=n;i++) 32 for(int j=a[i];j<=10000;j+=a[i]) 33 for(int k=vec[j].size()-1;k>=0;k--) 34 { 35 if(vec[j][k]<=i) break; 36 else L[vec[j][k]]=max(L[vec[j][k]],i); 37 } 38 LL ans=0; 39 for(int i=1;i<=n;i++) ans= ( ans+ (LL)(R[i]-i) * (LL)(i-L[i]) )%MOD; 40 printf("%I64d\n",ans); 41 } 42 return 0; 43 }
1002 Assignment
先用ST表处理好所有区间的最大值最小值。
枚举区间的左端点L,区间内最大值与最小值的差值随着右端点的增加单调递增。
于是可以二分出合法的右端点最大值Rmax。
找到右端点的最大值,则只要右端点R满足L<=R<=Rmax的区间均可。
即找出每组L,Rmax对ans的贡献是Rmax-L+1。
答案超出了int范围。注意LL。
【跑完时间并不宽裕。慎用线段树与树状数组。】
【先不说ST表。感觉自己二分一直写很丑阿。】
1 # include <iostream> 2 # include <cstdio> 3 # include <cmath> 4 # include <algorithm> 5 using namespace std; 6 typedef long long LL; 7 # define maxn 100010 8 LL a[maxn],MAX[maxn][20],MIN[maxn][20]; 9 10 int main(void) 11 { 12 int T; cin>>T; 13 while(T--) 14 { 15 int n,k; scanf("%d%d",&n,&k); 16 for(int i=1;i<=n;i++) 17 { 18 scanf("%I64d",a+i); 19 MAX[i][0]=MIN[i][0]=a[i]; 20 } 21 for(int j=1;j<20;j++) 22 for(int i=1;i<=n;i++) 23 if(i+(1<<j)-1<=n) 24 { 25 MAX[i][j]=max(MAX[i][j-1],MAX[i+(1<<j-1)][j-1]); 26 MIN[i][j]=min(MIN[i][j-1],MIN[i+(1<<j-1)][j-1]); 27 } 28 LL ans=0; 29 for(int i=1;i<=n;i++) 30 { 31 int l=i,r=n; 32 while(l<r) 33 { 34 int mid=(l+r)/2; 35 int t=(log(double(mid-i+1))/log(2)); 36 LL tem=max(MAX[i][t],MAX[mid-(1<<t)+1][t])-min(MIN[i][t],MIN[mid-(1<<t)+1][t]); 37 if(tem>=k) r=mid-1; 38 else l=mid+1; 39 } 40 int t=(log(double(l-i+1))/log(2)); 41 LL tem=max(MAX[i][t],MAX[l-(1<<t)+1][t])-min(MIN[i][t],MIN[l-(1<<t)+1][t]); 42 if(tem>=k) l--; 43 ans+=l-i+1; 44 } 45 printf("%I64d\n",ans); 46 } 47 return 0; 48 }
1003 Bombing plan
1004 Candy Distribution
1005 Pocket Cube
前面百度之星做过一个魔方。
纯模拟即可。也有看色向的优化方法。但是并没有去了解。
这题一看就知道是判色向的。然而并不知道怎么算QAQ。
【比赛的时候学姐竟然还叫我看一下这个题目。太看得起我了。】
题解很良心的证明了一下定理。想再讲一下自己的理解。
因为只会三阶公式。我一般是把二阶魔方看成三阶的角块。
所有安装正确的二阶魔方是一个等价类。通过旋转置换还原。
【二阶有自己更为简洁的公式。】
首先。必定能做到将底层还原。
其次。运用角块归位公式。将角块的位置摆对。不看色向。(即官方题解图3)
【上面两步即便是装错的也能做到。】
最后。顶块翻色。这个公式是两个相邻的角块同时向内侧翻色。(即官方题解图2)
一个安装正确的魔方只要反复翻色。每次至少使一个角块颜色正确。便能还原。
错误安装的魔方翻色至最后一个角块的色向是顺时针或者逆时针的。
证明的主要内容就是说明了。这个翻色的操作不会改变色向和%3的值。
而一个还原的二阶魔方色相和%3是为零的。
因此只有色相和%3为零的魔方才是正确安装魔方的等价类。
1 # include <iostream> 2 # include <cstdio> 3 using namespace std; 4 5 int main(void) 6 { 7 int T; cin>>T; 8 for(int kase=1;kase<=T;kase++) 9 { 10 int sum=0; 11 for(int i=1;i<=24;i++) 12 { 13 char color[5]; 14 scanf(" %s",color); 15 if(color[0]=='w'||color[0]=='y') 16 { 17 if(i==1||i==4||i==6||i==10||i==11||i==15||i==17||i==20) sum++; 18 else if(i==2||i==3||i==5||i==9||i==12||i==16||i==18||i==19) sum--; 19 } 20 } 21 printf("Case #%d: ",kase); 22 if(sum%3==0) printf("YES\n"); 23 else printf("NO\n"); 24 } 25 return 0; 26 }
1006 Tree chain problem
码这个真的超级不容易。希望我能把这个问题讲清楚。
菊苣们必然是有很厉害的方法的。
对于我等渣而言。做这个题至少要了解以下内容。
会些dp。时间戳。会一个区间求和工具(线段树或者树状数组。LCA(离线。在线。
我对dp了解并不多。求和图方便用的树状数组。LCA只会离线的。
首先要理解题意。题目中u,v间链是从u出发向上绕过LCA(u,v)再往下连到v的。
(一开始我连sample都看不懂。
整体上是一个树形dp。看成1为根的有根树。
用dp[i]表示以i为根节点的子树的答案。那么我们要求的是dp[1]。
为了方便。引进了sum[i]=sigma(dp[k]|k为i的孩子)。
状态转移是这样的。
如果我们取的链不经过i。
那么dp[i]=sum[i]。即子树上的链重和就是答案。
如果我们要取经过i的链。注意到dp[i]的含义是以i为根节点的子树的答案。
既然i是根了。而且链经过了i。那么i必然也是链两端节点的LCA了。
取了这条链之后。对于链上的每个节点j。我们就不能再取经过它们的链了。
那么它们对dp[i]的贡献就由原来的dp[j]减少为sum[j]。
所以我们在sum[i]的基础上先减去sigma(dp[j]|j为链上的节点)再加回sigma(sum[j]|j为链上的节点)。
考虑完所有的链。
dp[i]=max(sum[i],sum[i]+weight-sigma(dp[j]|j为链上的节点)+sigma(sum[j]|j为链上的节点))。
这里官方题解似乎忘记加上sum[i]了。
到这里我们还有两个问题。
1.如何处理LCA。
2.计算sigma很大程度上决定了总效率。如何求和。
LCA我用的是离线的算法。
用一个结构体向量存以每个节点为LCA的链的端点和重量。
每找出一个LCA往向量里push。dp的时候扫一遍即可。
求和这里用到了时间戳的一些性质。
1007 Tricks Device
1008 Unstable
1009 Annoying problem
1010 Y sequence
1012 Circles Game