2015 Multi-University Training Contest 1

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的因子先处理。】

2015 Multi-University Training Contest 1
 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 }
Aguin

 

 

1002 Assignment

先用ST表处理好所有区间的最大值最小值。

枚举区间的左端点L,区间内最大值与最小值的差值随着右端点的增加单调递增。

于是可以二分出合法的右端点最大值Rmax。

找到右端点的最大值,则只要右端点R满足L<=R<=Rmax的区间均可。

即找出每组L,Rmax对ans的贡献是Rmax-L+1。

答案超出了int范围。注意LL。

【跑完时间并不宽裕。慎用线段树与树状数组。】

【先不说ST表。感觉自己二分一直写很丑阿。】

2015 Multi-University Training Contest 1
 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 }
Aguin

 

 

1003 Bombing plan

 

 

1004 Candy Distribution

 

 

1005 Pocket Cube

前面百度之星做过一个魔方。

纯模拟即可。也有看色向的优化方法。但是并没有去了解。

这题一看就知道是判色向的。然而并不知道怎么算QAQ。

【比赛的时候学姐竟然还叫我看一下这个题目。太看得起我了。】

题解很良心的证明了一下定理。想再讲一下自己的理解。

因为只会三阶公式。我一般是把二阶魔方看成三阶的角块。

所有安装正确的二阶魔方是一个等价类。通过旋转置换还原。

【二阶有自己更为简洁的公式。】

首先。必定能做到将底层还原。

其次。运用角块归位公式。将角块的位置摆对。不看色向。(即官方题解图3)

【上面两步即便是装错的也能做到。】

最后。顶块翻色。这个公式是两个相邻的角块同时向内侧翻色。(即官方题解图2)

一个安装正确的魔方只要反复翻色。每次至少使一个角块颜色正确。便能还原。

错误安装的魔方翻色至最后一个角块的色向是顺时针或者逆时针的。

证明的主要内容就是说明了。这个翻色的操作不会改变色向和%3的值。

而一个还原的二阶魔方色相和%3是为零的。

因此只有色相和%3为零的魔方才是正确安装魔方的等价类。

2015 Multi-University Training Contest 1
 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 }
Aguin

 

 

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

 

1011 Solid Geometry Homework

 

1012 Circles Game

 

你可能感兴趣的:(test)