Contest14的本質:區間覆蓋+Tarjan(ios
把距離公式兩邊平方便可數組
注意要long long優化
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #define fo(a,b,c) for (a=b; a<=c; a++) #define fd(a,b,c) for (a=b; a>=c; a--) using namespace std; int x[100001]; int y[100001]; int n,i,j,k,l,X,Y,R,ans; int main() { // freopen("a.in","r",stdin); scanf("%d%d%d%d",&n,&X,&Y,&R); fo(i,1,n) scanf("%d%d",&x[i],&y[i]); fo(i,1,n) if (((long long)(x[i]-X)*(x[i]-X)+(long long)(y[i]-Y)*(y[i]-Y))<=(long long)R*R) ++ans; printf("%d\n",ans); }
顯然p和k是位置最靠邊的兩個spa
考慮一下i和n的位置(靠左/靠右/左右)code
比較醜排序
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #define fo(a,b,c) for (a=b; a<=c; a++) #define fd(a,b,c) for (a=b; a>=c; a--) #define max(a,b) (a>b?a:b) using namespace std; char a[1000001]; int T,n,i,j,k,l,ans,s1,s2,s3,s4; char ch; int main() { // freopen("b.in","r",stdin); scanf("%d",&T); for (;T;--T) { ans=-1; scanf("%d",&n); fo(i,1,n) { ch=getchar(); while (ch<'a' || ch>'z') ch=getchar(); a[i]=ch; } fo(i,1,n) if (a[i]=='p') break; fd(j,n,1) if (a[j]=='k') break; fo(k,i+1,n) if (a[k]=='i') break; fo(l,k+1,n) if (a[l]=='n') break; if (i>=1 && i<k && k<l && l<j && j<=n && a[i]=='p' && a[k]=='i' && a[l]=='n' && a[j]=='k') ans=max(ans,max(max(k-i-1,l-k-1),j-l-1)); fd(l,j-1,k+1) if (a[l]=='n') break; if (i>=1 && i<k && k<l && l<j && j<=n && a[i]=='p' && a[k]=='i' && a[l]=='n' && a[j]=='k') ans=max(ans,max(max(k-i-1,l-k-1),j-l-1)); fd(l,j-1,1) if (a[l]=='n') break; fd(k,l-1,i+1) if (a[k]=='i') break; if (i>=1 && i<k && k<l && l<j && j<=n && a[i]=='p' && a[k]=='i' && a[l]=='n' && a[j]=='k') ans=max(ans,max(max(k-i-1,l-k-1),j-l-1)); printf("%d\n",ans); } }
區間覆蓋*1get
一開始覺得一對區間只能算一次答案string
維護斷點,那麼一種方案=斷點數-1(加上首尾)it
一次覆蓋後,區間外的不變,區間內的變爲0,邊界變爲1io
因此能夠合在一塊兒維護,區間外的*2,區間內的不變,邊界+2^(i-1)
i的答案爲斷點總數-2^i
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #define fo(a,b,c) for (a=b; a<=c; a++) #define fd(a,b,c) for (a=b; a>=c; a--) #define mod 20050321 using namespace std; long long p[2001]; int L[2001]; int R[2001]; bool b[2001]; long long sum[2001]; long long Sum[2001]; int n,m,i,j,k,l; long long ans; int main() { // freopen("c.in","r",stdin); scanf("%d%d",&n,&m); p[0]=1; fo(i,1,m) { scanf("%d%d",&L[i],&R[i]); p[i]=p[i-1]*2%mod; } sum[0]=sum[n]=1; fo(i,1,m) { fo(j,0,n) Sum[j]=sum[j]; fo(j,0,L[i]-2) Sum[j]=(Sum[j]+sum[j])%mod; fo(j,R[i]+1,n) Sum[j]=(Sum[j]+sum[j])%mod; Sum[L[i]-1]=(Sum[L[i]-1]+p[i-1])%mod; Sum[R[i]]=(Sum[R[i]]+p[i-1])%mod; ans=0; fo(j,0,n) sum[j]=Sum[j],Sum[j]=0,ans=(ans+sum[j])%mod; printf("%lld\n",(ans-p[i]+mod)%mod); } }
區間覆蓋*2
套路,詢問若一段操做的結果就把詢問離線按r排序,每次加一個操做計算答案
維護每一段區間的兩個端點(左右括號),那麼每加一個區間最多加4個括號,每一個括號只會被刪一次
每加一個區間,就至關於把中間的端點刪掉(大的能覆蓋掉小的),再在兩邊加上新的端點
詢問就是求每一個修改所剩餘的個數*權值的後綴和
用set維護括號,每次斷開後找中間的區間,用樹狀數組維護答案
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <set> #define fo(a,b,c) for (a=b; a<=c; a++) #define fd(a,b,c) for (a=b; a>=c; a--) #define low(x) (x&-(x)) using namespace std; struct qs{ int l,r,id; } q[500001]; struct type{ int x,s,t; //0=right 1=left bool friend operator < (type a,type b) {return a.x<b.x || a.x==b.x && a.t<b.t;} }; int a[500001][3]; long long ans[500001]; long long tr[500001]; multiset<type> st; multiset<type> :: iterator I,J; int n,m,Q,i,j,k,l; bool cmp(qs a,qs b) { return a.r<b.r; } void change(int t,long long s) { while (t<=n) { tr[t]+=s; t+=low(t); } } long long find(int t) { long long ans=0; while (t) { ans+=tr[t]; t-=low(t); } return ans; } int main() { // freopen("d.in","r",stdin); scanf("%d%d%d",&n,&m,&Q); fo(i,1,n) scanf("%d%d%d",&a[i][0],&a[i][1],&a[i][2]); fo(i,1,Q) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; sort(q+1,q+Q+1,cmp); st.insert({0,0,1}); st.insert({m,0,0}); fo(i,1,Q) { fo(j,q[i-1].r+1,q[i].r) { I=st.lower_bound({a[j][0]-1,0,1}); if (I!=st.begin()) { --I; if ((*I).x<a[j][0]-1) { k=(*I).s; st.insert({a[j][0]-1,k,0}); st.insert({a[j][0]-1,k,1}); } } I=st.upper_bound({a[j][1],0,0}); if (I!=st.end()) { if (a[j][1]<(*I).x) { k=(*I).s; st.insert({a[j][1],k,0}); st.insert({a[j][1],k,1}); } } I=st.lower_bound({a[j][0]-1,0,1}); J=st.upper_bound({(*I).x,0,1}); while (I!=st.end() && (*I).x<a[j][1]) { if ((*I).s) change((*I).s,-(long long)a[(*I).s][2]*((*J).x-(*I).x)); st.erase(I); J=st.lower_bound({a[j][0]-1,0,1}); st.erase(J); I=st.lower_bound({a[j][0]-1,0,1}); J=st.upper_bound({(*I).x,0,1}); } st.insert({a[j][0]-1,j,1}); st.insert({a[j][1],j,0}); change(j,(long long)a[j][2]*(a[j][1]-a[j][0]+1)); } ans[q[i].id]=find(q[i].r)-find(q[i].l-1); } fo(i,1,Q) printf("%lld\n",ans[i]); }
Tarjan*1
這應該是除了那道簡單數論之外最水的E了
Tarjan縮強聯通份量,dp維護從起點到每一個點的最小邊權、最大邊權、最大差值
正確性:最大差值與路徑上的最大&最小值有關,那麼必定會在找到後面那個(大or小)的時候與另外一個計算到
注意細節,考慮極值所在位置(原點、邊、新點)
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #define fo(a,b,c) for (a=b; a<=c; a++) #define fd(a,b,c) for (a=b; a>=c; a--) #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) using namespace std; int a[500001][3]; int ls[200001]; int A[500001][3]; int Ls[200001]; int dfn[200001]; int low[200001]; int d[200001]; int D[200001]; int mx[200001]; int mn[200001]; bool bz[200001]; int num[200001]; int f[200001][3]; //mn,mx,ans int n,m,Q,i,j,k,l,len,Len,tot,h,t; void New(int x,int y,int z) { ++len; a[len][0]=y; a[len][1]=ls[x]; ls[x]=len; a[len][2]=z; } void NEW(int x,int y,int z) { ++Len; A[Len][0]=y; A[Len][1]=Ls[x]; Ls[x]=Len; A[Len][2]=z; ++D[y]; } void dfs(int t) { int i; ++j; dfn[t]=j; low[t]=j; bz[t]=1; d[++l]=t; for (i=ls[t]; i; i=a[i][1]) { if (!dfn[a[i][0]]) { dfs(a[i][0]); low[t]=min(low[t],low[a[i][0]]); } else if (bz[a[i][0]]) low[t]=min(low[t],dfn[a[i][0]]); } if (dfn[t]==low[t]) { ++tot; while (d[l]!=t) { bz[d[l]]=0; num[d[l--]]=tot; } bz[d[l]]=0; num[d[l--]]=tot; } } int main() { // freopen("e.in","r",stdin); // freopen("b.out","w",stdout); scanf("%d%d%d",&n,&m,&Q); fo(i,1,m) { scanf("%d%d%d",&j,&k,&l); New(j,k,l); } memset(mx,190,sizeof(mx)); memset(mn,60,sizeof(mn)); j=l=0; dfs(1); fo(j,1,n) if (num[j]) { for (i=ls[j]; i; i=a[i][1]) if (num[a[i][0]]) { if (num[j]==num[a[i][0]]) { mx[num[j]]=max(mx[num[j]],a[i][2]); mn[num[j]]=min(mn[num[j]],a[i][2]); } else NEW(num[j],num[a[i][0]],a[i][2]); } } h=t=0; fo(i,1,tot) if (!D[i]) d[++t]=i; fo(i,1,tot) { f[i][0]=mn[i]; f[i][1]=mx[i]; f[i][2]=f[i][1]-f[i][0]; } while (h<t) { for (i=Ls[d[++h]]; i; i=A[i][1]) { f[A[i][0]][0]=min(f[A[i][0]][0],min(f[d[h]][0],A[i][2])); f[A[i][0]][1]=max(f[A[i][0]][1],max(f[d[h]][1],A[i][2])); f[A[i][0]][2]=max(f[A[i][0]][2],A[i][2]-f[d[h]][0]); f[A[i][0]][2]=max(f[A[i][0]][2],mx[A[i][0]]-f[d[h]][0]); f[A[i][0]][2]=max(f[A[i][0]][2],mx[A[i][0]]-A[i][2]); f[A[i][0]][2]=max(f[A[i][0]][2],f[d[h]][1]-A[i][2]); f[A[i][0]][2]=max(f[A[i][0]][2],f[d[h]][1]-mn[A[i][0]]); f[A[i][0]][2]=max(f[A[i][0]][2],A[i][2]-mn[A[i][0]]); f[A[i][0]][2]=max(f[A[i][0]][2],max(f[d[h]][2],0)); --D[A[i][0]]; if (!D[A[i][0]]) d[++t]=A[i][0]; } } for (;Q;--Q) { scanf("%d",&j); if (num[j] && f[num[j]][2]>=0) printf("%d\n",f[num[j]][2]); else printf("-1\n"); } }
Tarjan*2
顯然能夠求出相交關係而後求割點,用主席樹優化連邊
口胡一下
按橫/豎順序掃描線,考慮相交線段a和b
a在加入時由葉子連向a,b在查找時由b連向詢問區間
每一個區間向兒子連邊,詢問時新建葉節點時就向原葉節點連邊,若是是刪除一段線段就不連
一次修改or詢問的操做節點和連邊數爲log n級別
兩種順序搞完以後Tarjan求割點便可
沒寫