A.雅礼集训convex
回滚莫队:莫队时若发现删除、添加中有一个容易操作(O(1)),有一个不容易操作(>O(1))就可以使用这种方法。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long int ll; 4 typedef long double ld; 5 const int maxn=150005; 6 int n,m; 7 int len,rk[maxn],bel[maxn]; 8 ll ans[maxn]; 9 struct pt 10 { 11 ll x,y; 12 int id; 13 ld ra; 14 ll operator*(const pt&A)const 15 { 16 return x*A.y-y*A.x; 17 } 18 bool operator<(const pt&A)const 19 { 20 return ra<A.ra; 21 } 22 }a[maxn],b[maxn]; 23 struct query 24 { 25 int l,r,id; 26 bool operator<(const query&A)const 27 { 28 return bel[l]==bel[A.l]?r>A.r:bel[l]<bel[A.l]; 29 } 30 }Q[maxn]; 31 inline void init() 32 { 33 for(int i=1;i<=n;++i) 34 a[i].ra=atan2(a[i].y,a[i].x); 35 sort(a+1,a+n+1); 36 for(int i=1;i<=n;++i) 37 rk[a[i].id]=i; 38 len=sqrt(n); 39 } 40 int tot,head[555]; 41 int pre[maxn],nxt[maxn]; 42 int tmp[maxn]; 43 ll now; 44 inline ll f(int x,int y) 45 { 46 return a[x]*a[y]; 47 } 48 inline void doPre(int l) 49 { 50 now=0; 51 memset(tmp,0,sizeof(tmp)); 52 memset(pre,0,sizeof(pre)); 53 memset(nxt,0,sizeof(nxt)); 54 for(int i=l;i<=n;++i) 55 tmp[rk[i]]=i; 56 int last=0,first=n+1; 57 for(int i=1;i<=n;++i) 58 if(tmp[i]) 59 { 60 if(last) 61 { 62 pre[i]=last; 63 nxt[last]=i; 64 now+=f(last,i); 65 } 66 last=i; 67 first=min(first,i); 68 } 69 pre[first]=last; 70 nxt[last]=first; 71 now+=f(last,first); 72 } 73 inline void rem(int pos) 74 { 75 now-=f(pre[pos],pos)+f(pos,nxt[pos]); 76 now+=f(pre[pos],nxt[pos]); 77 nxt[pre[pos]]=nxt[pos]; 78 pre[nxt[pos]]=pre[pos]; 79 } 80 inline ll ask(int L,int l) 81 { 82 // cout<<"?? "<<L<<" "<<l<<endl; 83 ll s=now; 84 for(int i=L;i<l;++i) 85 { 86 int pos=rk[i]; 87 s-=f(pre[pos],pos)+f(pos,nxt[pos]); 88 s+=f(pre[pos],nxt[pos]); 89 nxt[pre[pos]]=nxt[pos]; 90 pre[nxt[pos]]=pre[pos]; 91 } 92 for(int i=l-1;i>=L;--i) 93 { 94 int pos=rk[i]; 95 nxt[pre[pos]]=pos; 96 pre[nxt[pos]]=pos; 97 } 98 return s; 99 } 100 inline void solve() 101 { 102 for(int i=1;i<=n;i+=len) 103 { 104 ++tot; 105 bel[i]=tot; 106 head[tot]=i; 107 } 108 for(int i=1;i<=n;++i) 109 if(!bel[i]) 110 bel[i]=bel[i-1]; 111 sort(Q+1,Q+m+1); 112 int pos=1; 113 for(int i=1;i<=tot;++i) 114 { 115 doPre(head[i]); 116 int r=n; 117 while(bel[Q[pos].l]==i) 118 { 119 while(r>Q[pos].r) 120 rem(rk[r--]); 121 ans[Q[pos].id]=ask(head[i],Q[pos].l); 122 ++pos; 123 } 124 } 125 } 126 int main() 127 { 128 // freopen("convex.in","r",stdin); 129 // freopen("convex.out","w",stdout); 130 scanf("%d%d",&n,&m); 131 for(int i=1;i<=n;++i) 132 { 133 scanf("%lld%lld",&a[i].x,&a[i].y); 134 a[i].id=i; 135 } 136 for(int i=1;i<=m;++i) 137 { 138 scanf("%d%d",&Q[i].l,&Q[i].r); 139 Q[i].id=i; 140 } 141 init(); 142 solve(); 143 for(int i=1;i<=m;++i) 144 printf("%lld\n",ans[i]); 145 return 0; 146 }
B.雅礼集训进攻
容斥的想法:枚举最后的可行矩形,计算包含它的矩形个数,按照(-1)^(行数+列数)进行容斥。
但这样没有任何拓展性!这又一次说明了容斥是多么的菜。
直接统计的想法:尝试将难以考虑的整体转化为易于计数的部分。为了计算一个可行矩形的“1”的贡献,我们将其拆成1*1、1*2、2*1、2*2(这个可以重叠)的小矩形。可以发现,对于任何一个矩形,存在1=cnt(1*1)-cnt(1*2)-cnt(2*1)+cnt(2*2)。在所有可行情况中,某个位置上的cnt产生的总贡献是所有能够包含它的矩形的个数(当然,要k次方),而与包含了多少无关。
再考虑如何统计包含某个矩形的矩形个数。以1*1的矩形为例,最直接的方法是枚举所有可行的矩形,并把其中的每一个位置都加上1。我们把它差分成左上角加一、右上左下(要偏移一下)减一、右下加一,若我们能算出这个差分数组,就能在平方的复杂度内算出包含某个矩形的矩形个数。
显然加一减一只和((以某个矩形为某一角,包含这个矩形)的矩形个数)有关。栈即可。
1 #include<bits/stdc++.h> 2 #define mod 998244353 3 using namespace std; 4 typedef long long int ll; 5 const int maxn=2E3+5; 6 int n,m,k; 7 int a[maxn][maxn],sum[maxn][maxn],up[maxn][maxn],down[maxn][maxn]; 8 int tmp[maxn][maxn]; 9 ll ans; 10 inline ll qpow(ll x,ll y) 11 { 12 ll ans=1,base=x; 13 while(y) 14 { 15 if(y&1) 16 ans=ans*base%mod; 17 base=base*base%mod; 18 y>>=1; 19 } 20 return ans; 21 } 22 inline void init() 23 { 24 for(int j=1;j<=m;++j) 25 for(int i=1;i<=n;++i) 26 if(a[i][j]) 27 up[i][j]=up[i-1][j]+1; 28 for(int j=1;j<=m;++j) 29 for(int i=n;i>=1;--i) 30 if(a[i][j]) 31 down[i][j]=down[i+1][j]+1; 32 } 33 int b[maxn],q[maxn]; 34 inline ll get(int dx,int dy) 35 { 36 memset(tmp,0,sizeof(tmp)); 37 for(int i=1;i<=n;++i) 38 { 39 int sum=0,top=1; 40 q[top]=m+1; 41 for(int j=m;j>=1;--j) 42 { 43 b[j]=max(0,down[i][j]-dx+1); 44 while(top&&b[j]<=b[q[top]]) 45 { 46 sum-=(q[top-1]-q[top])*b[q[top]]; 47 --top; 48 } 49 sum+=(q[top]-j)*b[j]; 50 q[++top]=j; 51 tmp[i][j]+=sum-(dy==2?b[j]:0); 52 } 53 } 54 for(int i=1;i<=n;++i) 55 { 56 int sum=0,top=1; 57 q[top]=0; 58 for(int j=1;j<=m;++j) 59 { 60 b[j]=max(0,down[i][j]-dx+1); 61 while(top&&b[j]<=b[q[top]]) 62 { 63 sum-=(q[top]-q[top-1])*b[q[top]]; 64 --top; 65 } 66 sum+=(j-q[top])*b[j]; 67 q[++top]=j; 68 tmp[i][j-dy+2]-=sum-(dy==2?b[j]:0); 69 } 70 } 71 for(int i=1;i<=n;++i) 72 { 73 int sum=0,top=1; 74 q[top]=m+1; 75 for(int j=m;j>=1;--j) 76 { 77 b[j]=max(0,up[i][j]-dx+1); 78 while(top&&b[j]<=b[q[top]]) 79 { 80 sum-=(q[top-1]-q[top])*b[q[top]]; 81 --top; 82 } 83 sum+=(q[top]-j)*b[j]; 84 q[++top]=j; 85 tmp[i-dx+2][j]-=sum-(dy==2?b[j]:0); 86 } 87 } 88 for(int i=1;i<=n;++i) 89 { 90 int sum=0,top=1; 91 q[top]=0; 92 for(int j=1;j<=m;++j) 93 { 94 b[j]=max(0,up[i][j]-dx+1); 95 while(top&&b[j]<=b[q[top]]) 96 { 97 sum-=(q[top]-q[top-1])*b[q[top]]; 98 --top; 99 } 100 sum+=(j-q[top])*b[j]; 101 q[++top]=j; 102 tmp[i-dx+2][j-dy+2]+=sum-(dy==2?b[j]:0); 103 } 104 } 105 for(int i=1;i<=n;++i) 106 for(int j=1;j<=m;++j) 107 tmp[i][j]+=tmp[i-1][j]+tmp[i][j-1]-tmp[i-1][j-1]; 108 ll s=0; 109 for(int i=1;i<=n;++i) 110 for(int j=1;j<=m;++j) 111 s+=qpow(tmp[i][j],k); 112 return s; 113 } 114 inline void solve1() 115 { 116 ans=(ans+get(1,1))%mod; 117 } 118 inline void solve2() 119 { 120 ans=(ans-get(1,2))%mod; 121 ans=(ans-get(2,1))%mod; 122 } 123 inline void solve3() 124 { 125 ans=(ans+get(2,2))%mod; 126 } 127 inline void solve() 128 { 129 solve1(); 130 solve2(); 131 solve3(); 132 } 133 int main() 134 { 135 ios::sync_with_stdio(false); 136 cin>>n>>m>>k; 137 for(int i=1;i<=n;++i) 138 { 139 string str; 140 cin>>str; 141 for(int j=0;j<m;++j) 142 a[i][j+1]=str[j]-'0'; 143 } 144 init(); 145 solve(); 146 cout<<(ans%mod+mod)%mod<<endl; 147 return 0; 148 }
C.雅礼集训string
首先看清题意!
若我们算出了区间[l,r]的答案,那么新添或删去一个字符串相当于从trie树上新添或删去对应位置的字符串,然后统计增量。
发现添加难,删除简单,还是回滚莫队。
注意这里的分块要按串的总长来分。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long int ll; 4 const int maxn=3E5+5; 5 int n,m,ans[maxn]; 6 ll a[maxn],A,B,C,L,g[maxn]; 7 int len,tot,have[maxn],bel[maxn],tail[maxn],head[maxn],belW[maxn]; 8 char ch[maxn][26]; 9 string str[maxn]; 10 struct query 11 { 12 int l,r,id; 13 bool operator<(const query&A)const 14 { 15 return bel[tail[l-1]+1]==bel[tail[A.l-1]+1]?r>A.r:bel[tail[l-1]+1]<bel[tail[A.l-1]+1]; 16 } 17 }Q[maxn]; 18 inline void insert(string s) 19 { 20 int p=0; 21 for(int i=0;i<s.size();++i) 22 { 23 int x=s[i]-'a'; 24 if(!ch[p][x]) 25 ch[p][x]=++tot; 26 p=ch[p][x]; 27 } 28 } 29 inline void init() 30 { 31 len=sqrt(tail[n]); 32 for(int i=1;i<=tail[n];++i) 33 bel[i]=bel[i-1]+((i-1)%len==0); 34 sort(Q+1,Q+m+1); 35 } 36 inline bool ok(int f,int len) 37 { 38 return f?B*f+A*len>=C:0; 39 } 40 ll now; 41 int maxD,tmp[maxn],pre[maxn],nxt[maxn],inS[maxn]; 42 inline ll f(ll x,ll y) 43 { 44 return (x-y)*(x-y+1)/2; 45 } 46 int top,wait[maxn]; 47 inline void add(string str,int v) 48 { 49 int p=0,d=0; 50 for(int i=0;i<str.size();++i) 51 { 52 p=ch[p][str[i]-'a']; 53 ++d; 54 int x=ok(have[p],d); 55 have[p]+=v; 56 int y=ok(have[p],d); 57 if(x==1&&y==0) 58 { 59 x=g[d]; 60 --inS[x]; 61 if(inS[x]==0) 62 { 63 now-=f(pre[x],x)+f(x,nxt[x]); 64 now+=f(pre[x],nxt[x]); 65 pre[nxt[x]]=pre[x]; 66 nxt[pre[x]]=nxt[x]; 67 wait[++top]=x; 68 } 69 } 70 else if(x==0&&y==1) 71 ++inS[g[d]]; 72 } 73 } 74 inline void bend() 75 { 76 while(top) 77 { 78 int x=wait[top]; 79 pre[nxt[x]]=x; 80 nxt[pre[x]]=x; 81 --top; 82 } 83 } 84 inline void doPre(int l) 85 { 86 memset(tmp,0,sizeof(tmp)); 87 memset(inS,0,sizeof(inS)); 88 memset(nxt,0,sizeof(nxt)); 89 memset(pre,0,sizeof(pre)); 90 memset(have,0,sizeof(have)); 91 top=now=0; 92 for(int i=l;i<=n;++i) 93 { 94 int p=0,d=0,v=a[i]; 95 for(int j=0;j<str[i].size();++j) 96 { 97 p=ch[p][str[i][j]-'a']; 98 ++d; 99 have[p]+=v; 100 if(ok(have[p],d)) 101 tmp[g[d]]=1,++inS[g[d]]; 102 } 103 } 104 int last=0; 105 tmp[0]=tmp[maxD+1]=1; 106 for(int i=0;i<=maxD+1;++i) 107 if(tmp[i]) 108 { 109 now+=f(last,i); 110 nxt[last]=i; 111 pre[i]=last; 112 last=i; 113 } 114 } 115 inline void solve() 116 { 117 for(int i=tail[n];i>=1;--i) 118 head[bel[i]]=belW[i]; 119 int maxx=bel[tail[n]]; 120 int pos=1; 121 for(int i=1;i<=maxx;++i) 122 { 123 int r=n; 124 doPre(head[i]); 125 while(bel[tail[Q[pos].l-1]+1]==i) 126 { 127 cout<<"?!?! "<<now<<endl; 128 while(r>Q[pos].r) 129 { 130 add(str[r],-a[r]); 131 --r; 132 top=0; 133 } 134 int l=head[i]; 135 ll g=now; 136 while(l<Q[pos].l) 137 { 138 add(str[l],-a[l]); 139 ++l; 140 } 141 ans[Q[pos].id]=now; 142 bend(); 143 while(l>head[i]) 144 { 145 --l; 146 add(str[l],a[l]); 147 } 148 now=g; 149 ++pos; 150 } 151 } 152 } 153 ll gcd(ll x,ll y) 154 { 155 return x%y==0?y:gcd(y,x%y); 156 } 157 int main() 158 { 159 freopen("string1.in","r",stdin); 160 ios::sync_with_stdio(false); 161 cin>>n>>A>>B>>C; 162 for(int i=1;i<=n;++i) 163 cin>>a[i]; 164 for(int i=1;i<=n;++i) 165 { 166 cin>>str[i]; 167 insert(str[i]); 168 maxD=max(maxD,(int)str[i].size()); 169 tail[i]=tail[i-1]+str[i].size(); 170 for(int j=tail[i-1]+1;j<=tail[i];++j) 171 belW[j]=i; 172 } 173 for(int i=1;i<=maxD;++i) 174 cin>>g[i]; 175 cin>>m; 176 for(int i=1;i<=m;++i) 177 { 178 cin>>Q[i].l>>Q[i].r; 179 Q[i].id=i; 180 } 181 init(); 182 solve(); 183 ll tot=f(0,maxD+1); 184 for(int i=1;i<=m;++i) 185 { 186 ans[i]=tot-ans[i]; 187 ll d=gcd(ans[i],tot); 188 cout<<ans[i]/d<<"/"<<tot/d<<endl; 189 } 190 return 0; 191 }