Day1 T2:最大前缀和
枚举答案集合(不直接枚举答案数,相当于状态的离散化),这个集合成为答案当且仅当存在方案使得答案集合的排列后缀和>=0(如果<0就可以去掉显然更优),答案补集的前缀和<0(不能再延伸)。预处理方案数。最后统计的时候注意要枚举答案子集的第一个元素,它不需要满足后缀和>=0。O(2^n*n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int mod=998244353; 4 typedef long long ll; 5 const int N=1050000; 6 int n,Max,a[N],sum[N],f[N],g[N],ans; 7 void up(int &x,int y){x=((ll)x+y)%mod;} 8 int main() 9 { 10 scanf("%d",&n); 11 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 12 Max=1<<n; 13 for (int i=1;i<Max;i++) 14 for (int j=1;j<=n;j++) 15 if (i&(1<<(j-1))) up(sum[i],a[j]); 16 f[0]=1; 17 for (int i=1;i<Max;i++)//后缀和>=0 18 if (sum[i]>=0) 19 { 20 for (int j=1;j<=n;j++) 21 if (i&(1<<(j-1))) up(f[i],f[i^(1<<(j-1))]); 22 } 23 g[0]=1; 24 for (int i=1;i<Max;i++)//前缀和<0 25 if (sum[i]<0) 26 { 27 for (int j=1;j<=n;j++) 28 if (i&(1<<(j-1))) up(g[i],g[i^(1<<(j-1))]); 29 } 30 for (int i=1;i<Max;i++) 31 for (int j=1;j<=n;j++) 32 if (i&(1<<(j-1))) 33 up(ans,(ll)f[i^(1<<(j-1))]*g[i^(Max-1)]%mod*sum[i]%mod); 34 printf("%d\n",((ll)ans+mod)%mod);//注意ans有可能是负数! 35 return 0; 36 }
Day2
T1:星际穿越 贪心+树上倍增
要想到肯定贪心走能走的较远点。单调肯定不会让你好好求最短路,建树是一个思路。
性质:当起点在终点右边时,起点要走到终点,必然只会往右走一步,接下来一直往左走。
每个点朝能够到达的点中覆盖区间左端点最左的点连边,如果它想要走最远,选择这一点一定最优。
往右走一步也是类似的道理,一定是往右走能够走到的点中覆盖区间左端点最左的点最优。(具体实现可以按照覆盖区间左端点排序)如果往右再往左两步没有直接往左两步优的话,一定不会往右走。
如果一步可以走到也一定不会往右。(询问的时候特判一下即可)
询问的时候对于[1..r]求走到x的距离和。倍增使得x在[1..r]外且l[x]在[1..r]内,分成[1..l[x])和[l[x]..r]两段通过走到当前x再走到终点。预处理一下[1..l[i])到i的距离的和。O((n+q)logn)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=300005; 4 int l[N],a[N][25],par[N][25],n,pre[N],q,fa[N],mx,b[N],rt[N]; 5 int getmin(int x,int y) 6 { 7 return l[x]<l[y]?x:y; 8 } 9 void init() 10 { 11 for (int i=1;i<=n;i++) a[i][0]=i; 12 for (int j=1;j<=20;j++) 13 for (int i=1;i+(1<<j)-1<=n;i++) 14 a[i][j]=getmin(a[i][j-1],a[i+(1<<(j-1))][j-1]); 15 } 16 int qry(int l,int r) 17 { 18 int len=0; 19 while ((1<<(len+1))<=r-l+1) len++; 20 return getmin(a[l][len],a[r-(1<<len)+1][len]); 21 } 22 bool cmp(int A,int B) {return l[A]<l[B];} 23 int calc(int x,int y)//[1..x] to y 24 { 25 int ans=0; 26 if (rt[y]) 27 { 28 if (l[y]<=x) ans+=x-l[y]+1,x=l[y]-1; 29 y=rt[y];ans+=x; 30 } 31 if (!x) return ans; 32 int sum=0; 33 for (int i=20;i>=0;i--)//倍增使得l[终点]跳进[1..x] 34 if (l[par[y][i]]>x) y=par[y][i],sum+=(1<<i); 35 if (l[y]>x) y=par[y][0],sum++; 36 return ans+pre[y]+sum*(l[y]-1)+(sum+1)*(x-l[y]+1); 37 } 38 int main() 39 { 40 scanf("%d",&n); 41 for (int i=2;i<=n;i++) scanf("%d",&l[i]); 42 init(); 43 for (int i=2;i<=n;i++) fa[i]=par[i][0]=qry(l[i],i-1);//找到可以跳到最左边的点作为父亲 44 for (int j=1;j<=20;j++)//树上倍增 45 for (int i=1;i<=n;i++) 46 par[i][j]=par[par[i][j-1]][j-1]; 47 48 for (int i=1;i<=n;i++) b[i]=i;//处理会向右走一步的情况 49 sort(b+1,b+n+1,cmp); 50 for (int i=1;i<=n;i++) 51 { 52 for (int j=mx+1;j<b[i];j++) rt[j]=b[i]; 53 mx=max(mx,b[i]); 54 } 55 for (int i=1;i<=n;i++) 56 if (l[rt[i]]>=l[fa[i]]) rt[i]=0; 57 for (int i=1;i<=n;i++)//处理前缀和[1..l[i]) to i 58 if (fa[i]==1) pre[i]=0;else pre[i]=pre[fa[i]]+l[fa[i]]-1+2*(l[i]-l[fa[i]]); 59 60 scanf("%d",&q); 61 while (q--) 62 { 63 int l,r,x; 64 scanf("%d%d%d",&l,&r,&x); 65 int a=calc(r,x)-calc(l-1,x),b=r-l+1; 66 int Gcd=__gcd(a,b); 67 a/=Gcd;b/=Gcd;printf("%d/%d\n",a,b); 68 } 69 return 0; 70 }
T2:神仙的游戏
border相交有循环节的性质。问题是快速检查循环节的合法性。(我都想到bitset了T_T)
如果循环节长度为len时,对应位置的i,j一个为0一个为1,那么循环节长度为len的因数的都不可行。(有一个部分分给的就是暴力枚举01的位置)
fft处理对应位置的01是否有冲突,需要多项式平移。枚举当前循环节长度的倍数判断可行。时间复杂度O(nlogn)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod=998244353; 5 const int root=31; 6 const int N=2000005; 7 char s[N]; 8 int _n,l,w[N],pos[N],A[N],B[N],sl; 9 int ksm(int x,int y) 10 { 11 int res=1; 12 while (y) {if (y&1) res=(ll)res*x%mod; x=(ll)x*x%mod; y>>=1;} 13 return res; 14 } 15 void init(int n) 16 { 17 l=0; 18 while ((1<<l)<=n) l++; 19 _n=(1<<l);int ww=ksm(root,1<<23-l); 20 w[0]=1; 21 for (int i=1;i<_n;i++) 22 w[i]=(ll)w[i-1]*ww%mod, 23 pos[i]=(i&1)?pos[i-1]|(1<<(l-1)):pos[i>>1]>>1; 24 } 25 void fft(int *a) 26 { 27 for (int i=0;i<_n;i++) if (i<pos[i]) swap(a[i],a[pos[i]]); 28 int len=1,id=_n; 29 for (int i=0;i<l;i++) 30 { 31 int wn=w[id>>=1]; 32 for (int j=0;j<_n;j+=len*2) 33 for (int k=j,ww=1;k<j+len;k++) 34 { 35 int l=a[k],r=(ll)a[k+len]*ww%mod; 36 a[k]=((ll)l+r)%mod;a[k+len]=((ll)l-r+mod)%mod; 37 ww=(ll)ww*wn%mod; 38 } 39 len<<=1; 40 } 41 } 42 int main() 43 { 44 scanf("%s",s+1);sl=strlen(s+1); 45 for (int i=1;i<=sl;i++) 46 if (s[i]=='1') A[i]=1; 47 else if (s[i]=='0') B[sl-i+1]=1; 48 init(sl*2+1);fft(A);fft(B); 49 for (int i=0;i<_n;i++) A[i]=(ll)A[i]*B[i]%mod; 50 fft(A);reverse(A+1,A+_n); 51 int inv_n=ksm(_n,mod-2); 52 for (int i=0;i<_n;i++) A[i]=(ll)A[i]*inv_n%mod; 53 ll ans=(ll)sl*sl; 54 for (int i=1;i<sl;i++) 55 { 56 int fl=1; 57 for (int j=i;j<=sl&&fl;j+=i) 58 fl&=(!A[sl-j+1]&&!A[sl+j+1]); 59 if (fl) ans^=(ll)(sl-i)*(sl-i); 60 } 61 printf("%lld\n",ans); 62 return 0; 63 }