9.21题解

T1

首先谴责毒瘤出题人,说好的$k{\leq}10^5$,我最后却被迫枚举到了$10^5+1$,毒瘤防$AK$出题人

正解分块,对于不完整的块,暴力跑,非常的没问题,那为了保证分块的复杂度,我们需要$O(1)$查询一个完整的块中对于不同的$k$的答案,考虑一下如何预处理这个东西,一个很简单的结论,${\%}k$意义下的答案一定是$[0,k-1],[k,2k-1],[2k,3k-1],{\cdots},[xk,(x+1){\times}k-1]$这些每个区间中的最大的数,也就是区间中${\%}k$之后的最大值再取最大值,我们怎么能预处理出每个区间中的最大值呢,首先肯定需要枚举$k$,也可以离线,但是没什么必要,对于一个特定的$k$,我们需要知道每个区间中的最大值,我们开个桶,记录每个艳丽度,即$tong[a[i]]=a[i]$,让他等于$a[i]$的原因是为了方便后面的递推,考虑一下如果有一个数没出现过,那么我们就是找它的前一个数,以此类推,就是一个传递的过程,所以我们可以想到如果$tong[i]=0$也就是块内没有出现过$i$,那么$tong[i]=tong[i-1]$,这样的话,我们直接查$tong[k-1]$,更新块的答案即可,关于复杂度的证明似乎是什么调和级数,我不会

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #define maxn 200100
 6 using namespace std;
 7 int n,m,len=1500,sum;
 8 int so[maxn],tong[maxn];
 9 int dp[500][maxn];
10 inline int read()
11 {
12     int e=0,f=1;  char ch=getchar();
13     while(ch<'0'||ch>'9')
14     {
15         if(ch=='-')  f=-1;
16         ch=getchar();
17     }
18     while(ch>='0'&&ch<='9')  {e=(e<<3)+(e<<1)+(ch^48);  ch=getchar();}
19     return e*f;
20 }
21 int main()
22 {
23     //freopen("flower7.in","r",stdin);
24     //freopen("1.out","w",stdout);
25     n=read();  m=read();
26     //cout<<m<<endl;
27     for(int i=1;i<=n;++i)  so[i]=read();
28     if(n%len)  sum=n/len+1;
29     else  sum=n/len;
30     for(int i=1;i<=sum;++i)
31     {
32         int l=(i-1)*len+1,r=min(n,i*len);
33         for(int j=l;j<=r;++j)  tong[so[j]]=so[j];
34         for(int j=1;j<=100001;++j)
35             if(!tong[j])  tong[j]=tong[j-1];
36         for(int j=1;j<=100001;++j)
37         {
38             for(int o=j-1;o<=100001;o+=j)  dp[i][j]=max(dp[i][j],tong[o]%j);
39             dp[i][j]=max(dp[i][j],tong[100001]%j);
40         }
41         for(int j=1;j<=100001;++j)  tong[j]=0;
42     }
43     while(m--)
44     {
45         int z=read(),y=read(),k=read(),ans=0;
46         int qd=(z+len-1)/len,zd=(y+len-1)/len;
47         for(int i=qd+1;i<=zd-1;++i)  ans=max(ans,dp[i][k]);
48         for(int j=z;j<=min(qd*len,y);++j)  ans=max(ans,so[j]%k);
49         for(int j=max((zd-1)*len+1,z);j<=y;++j)  ans=max(ans,so[j]%k);
50         printf("%d\n",ans);
51     }
52     return 0;
53 }
View Code

T2

考场上打了个$dp$,但是不够优秀,正解是个$dp$,但显然跟我的sb$dp$不一样,我考场上考虑的是直接按照$y$排序,这样的话就只需要考虑$x$的影响,但是正解是按$x$排序,设$dp[i][0/1]$代表以$i$为一条折线的起点,向左或向右转的方案数,因为我们按照$x$排序,所以新的一个点肯定在前面点的右边,那么分两种情况,新点在之前的点的上面和新点在之前的点的下面

设$i$为新点,$j$为$i$前面的点,我们倒序枚举$j$

1.新点在上  $f[i][0]+=f[j][1]$

2.新点在下  $f[j][1]+=f[i][0]$

思考一下为什么倒序枚举,倒序枚举的话,$f[i][0]$中计算的方案的所有已经连上的点都比没连的点靠右,由于我当前这个新点在下,也就是说是$i$连$j$,但同时也可以是$i-1$连$j$,那你相当与计算了新的$j$可以连的所有的点而不仅仅是$i$,也就是相当与前缀和优化掉了一层循环

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 #define maxn 6060
 5 #define ll long long
 6 #define mod 1000000007
 7 using namespace std;
 8 struct node{
 9     int x,y;
10 }zb[maxn];
11 int n;
12 ll ans;
13 ll dp[maxn][2];
14 bool cmp(const node &a,const node &b)
15 {
16     return a.x<b.x;
17 }
18 int main()
19 {
20     scanf("%d",&n);
21     for(int i=1;i<=n;++i)  scanf("%d%d",&zb[i].x,&zb[i].y);
22     sort(zb+1,zb+n+1,cmp);
23     for(int i=1;i<=n;++i)  {dp[i][0]=1ll;  dp[i][1]=1ll;}
24     for(int i=1;i<=n;++i)
25     {
26         for(int j=i-1;j>=1;--j)
27         {
28             if(zb[j].y>zb[i].y)  dp[j][1]=(dp[i][0]+dp[j][1])%mod;
29             else  dp[i][0]=(dp[j][1]+dp[i][0])%mod;
30         }
31     }
32     for(int i=1;i<=n;++i)  {ans=(ans+dp[i][0])%mod;  ans=(ans+dp[i][1])%mod;}
33     ans=(ans-n+mod)%mod;  printf("%lld\n",ans);
34     return 0;
35 }
View Code

 T3

题解说不难猜到一个结论我怎么猜啊存在一种最优方案使得每次操作的区域是上一次的子集且颜色相反,证明我就直接贴了

我们考虑逆向这个过程,把白布的一些部分染黑,和把有一些部分是黑色的白布染成全白,实质上是一样的

由于联通块的特性,同一个颜色且联通的可以同时被染成另一种颜色,考虑建图跑最短路,同色边权为$0$,异色边权为$1$,对于边权只为$0$或$1$的,可以选择$01BFS$,用双端队列维护,可以省去一个$log$,最后的答案就是最远的黑点的距离最小值

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<deque>
 5 #define maxn 55
 6 #define bh(i,j)  ((i-1)*c+j)
 7 using namespace std;
 8 int r,c,js,ans=0x7fffffff;
 9 int x[5]={0,0,0,1,-1},y[5]={0,1,-1,0,0};
10 int dis[maxn*maxn],vis[maxn*maxn];
11 char s[maxn];
12 int a[maxn][maxn];
13 int get_x(int pos)
14 {
15     return (pos%c)==0?(pos/c):(pos/c+1);
16 }
17 int get_y(int pos)
18 {
19     return (pos%c)==0?c:(pos%c);
20 }
21 bool check(int h,int z)
22 {
23     if(h>=1&&h<=r&&z>=1&&z<=c)  return 1;
24     return 0;
25 }
26 void solve(int xx,int yy,int num)
27 {
28     deque <int> s;
29     int pos=bh(xx,yy),da=0;
30     dis[pos]=0;  s.push_front(pos);  vis[pos]=num;
31     while(s.size())
32     {
33         int ls=s.front();  s.pop_front();
34         int n=get_x(ls),m=get_y(ls);
35         if(a[n][m])  da=max(da,dis[ls]);
36         for(int i=1;i<=4;++i)
37         {
38             int BH=bh(n+x[i],m+y[i]);
39             if(check(n+x[i],m+y[i])&&(vis[BH]!=num))
40             {
41                 int cost=0;  vis[BH]=num;
42                 if(a[n+x[i]][m+y[i]]!=a[n][m])  cost=1;
43                 dis[BH]=dis[ls]+cost;
44                 if(cost)  s.push_back(BH);
45                 else  s.push_front(BH);
46             }
47         }
48     }
49     ans=min(ans,da);
50 }
51 int main()
52 {
53     //freopen("3.in","r",stdin);
54     //freopen("W.out","w",stdout);
55     scanf("%d%d",&r,&c);
56     for(int i=1;i<=r;++i)
57     {
58         scanf("%s",s+1);
59         for(int j=1;j<=c;++j)  a[i][j]=s[j]-'0';
60     }
61     for(int i=1;i<=r;++i)
62         for(int j=1;j<=c;++j)  {js++;  solve(i,j,js);}
63     printf("%d\n",ans+1);
64     return 0;
65 }
View Code

 

posted @ 2019-10-22 06:17  hzoi_X&R  阅读(128)  评论(0编辑  收藏  举报