A:阅读理解。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 1010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,a[N],b[N]; signed main() { /*#ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #endif*/ n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=n;i++) b[i]=read(); if (a[1]) { if (a[m]) {cout<<"YES";return 0;} if (b[m]) { for (int i=m+1;i<=n;i++) if (a[i]&&b[i]) {cout<<"YES";return 0;} } } cout<<"NO"; return 0; //NOTICE LONG LONG!!!!! }
B:即维护一个01序列有多少段1,瞎算算即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 100010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,k,ans; ll a[N]; signed main() { /*#ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #endif*/ n=read(),m=read(),k=read(); for (int i=1;i<=n;i++) a[i]=read()-k; for (int i=1;i<=n;i++) if (a[i]>0&&a[i-1]<=0) ans++; for (int i=1;i<=m;i++) { int op=read(); if (op==0) { printf("%d ",ans); } else { int x=read(),y=read(); if (a[x]<=0) { a[x]+=y; if (a[x]>0) ans++,ans-=a[x-1]>0,ans-=a[x+1]>0; } } } return 0; //NOTICE LONG LONG!!!!! }
C:任取两人各一段连续+区间,容易发现起始位置之差在模gcd(ta,tb)意义下总是相同的。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int l1,r1,t1,l2,r2,t2; ll cross(ll l1,ll r1,ll l2,ll r2) { if (l1>l2) swap(l1,l2),swap(r1,r2); return max(min(r2,r1)-l2+1,0ll); } signed main() { /*#ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #endif*/ l1=read(),r1=read(),t1=read(),l2=read(),r2=read(),t2=read(); int x=gcd(t1,t2); ll ans=cross(l1,r1,l2,r2); int u=(l2-l1)/x; ans=max(ans,cross(l1,r1,l2-1ll*u*x,r2-1ll*u*x)); u--; ans=max(ans,cross(l1,r1,l2-1ll*u*x,r2-1ll*u*x)); u++,u++; ans=max(ans,cross(l1,r1,l2-1ll*u*x,r2-1ll*u*x)); cout<<ans; return 0; //NOTICE LONG LONG!!!!! }
D:一个比较坑的题。容易想到随便找一个需要改的字符串,对其最短修改串(即去掉前后缀极长不需修改的部分)进行修改,判一下是否对其它串也合法就做完了。但问题是这样并不一定最优,可能需要延长修改部分,达到避免对某些串修改的目的,如ac→bc a→a,不应选a→b而应选ac→bc。于是考虑先把所有不需要改的串去掉,需要改的串求出最短修改串,如果任意一对最短修改串不同即无解。否则由每个最短修改串尽量往两边延伸,得到合法的极长修改串,再对其进行检验即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 3010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,u,nxt[N],L[N],R[N],len[N]; char a[N][N],b[N][N],anss[N],anst[N]; bool flag[N]; signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #endif n=read(); for (int i=1;i<=n;i++) scanf("%s",a[i]+1); for (int i=1;i<=n;i++) scanf("%s",b[i]+1); for (int i=1;i<=n;i++) { int m=strlen(a[i]+1);len[i]=m; flag[i]=1; for (int j=1;j<=m;j++) if (a[i][j]!=b[i][j]) {flag[i]=0;break;} if (!flag[i]) { int t; for (int k=1;k<=m;k++) if (a[i][k]!=b[i][k]) { t=m; while (a[i][t]==b[i][t]) t--; L[i]=k,R[i]=t; if (!u) u=t-k+1;else if (t-k+1!=u) {cout<<"NO";return 0;} break; } } } int l=0,r=0; for (int i=1;i<=3001;i++) { bool f=0; for (int j=1;j<=n;j++) if (!flag[j]&&L[j]==i) {f=1;break;} if (f) {l=i-1;break;} char c; for (int j=1;j<=n;j++) if (!flag[j]) {c=a[j][L[j]-i];break;} for (int j=1;j<=n;j++) if (!flag[j]) if (c!=a[j][L[j]-i]) {f=1;break;} if (f) {l=i-1;break;} } for (int i=1;i<=3001;i++) { bool f=0; for (int j=1;j<=n;j++) if (!flag[j]&&R[j]+i>len[j]) {f=1;break;} if (f) {r=i-1;break;} char c; for (int j=1;j<=n;j++) if (!flag[j]) {c=a[j][R[j]+i];break;} for (int j=1;j<=n;j++) if (!flag[j]) if (c!=a[j][R[j]+i]) {f=1;break;} if (f) {r=i-1;break;} } for (int i=1;i<=n;i++) if (!flag[i]) { int v=L[i]-l;u+=l+r; for (int j=1;j<=u;j++) anss[j]=a[i][v+j-1],anst[j]=b[i][v+j-1]; break; } nxt[0]=-1; for (int i=1;i<=u;i++) { int j=nxt[i-1]; while (~j&&anss[j+1]!=anss[i]) j=nxt[j]; nxt[i]=j+1; } for (int i=1;i<=n;i++) { int cur=0,m=len[i]; for (int j=1;j<=m;j++) { while (~cur&&a[i][j]!=anss[cur+1]) cur=nxt[cur]; cur++; if (cur==u) { for (int k=j-u+1;k<=j;k++) a[i][k]=anst[k-(j-u+1)+1]; break; } } for (int j=1;j<=m;j++) if (a[i][j]!=b[i][j]) {cout<<"NO";return 0;} } cout<<"YES"<<endl; printf("%s ",anss+1);printf("%s ",anst+1); return 0; //NOTICE LONG LONG!!!!! }
E:显然二分答案转换成01序列上的问题,然后问题就变为是否能从s个区间中选m个使其并区间的数字和>=k。显然可以合并掉有包含关系的区间后dp,具体地,设f[i][j]为前i个区间选j个时最多能有多少个1,考虑上个区间与当前区间是否有交,分两种情况转移,没有交直接取最大值,有交的情况用堆(或者单调队列?反正cf跑得快)维护。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; #define ll long long #define N 1510 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,s,k,a[N],c[N],f[N][N],ans=-1; bool flag[N]; struct data { int x,y; bool operator <(const data&a) const { return x<a.x||x==a.x&&y<a.y; } bool operator ==(const data&a) const { return x==a.x&&y==a.y; } }b[N],d[N]; priority_queue<int> ins,del; bool check(int u) { for (int i=1;i<=n;i++) if (a[i]<=u) c[i]=1;else c[i]=0; for (int i=1;i<=n;i++) c[i]+=c[i-1]; memset(f,0,sizeof(f)); for (int j=1;j<=s;j++) { int x=0,t=0; while (!ins.empty()) ins.pop(); while (!del.empty()) del.pop(); for (int i=1;i<=m;i++) { while (x<i&&b[x+1].y<b[i].x) x++,del.push(f[x][j-1]-c[b[x].y]),t=max(t,f[x][j-1]); while (!del.empty()&&ins.top()==del.top()) ins.pop(),del.pop(); f[i][j]=max(t+c[b[i].y]-c[b[i].x-1],c[b[i].y]+(ins.empty()?-1000000:ins.top())); ins.push(f[i][j-1]-c[b[i].y]); } } for (int i=1;i<=m;i++) if (f[i][s]>=k) return 1; return 0; } signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #endif n=read(),m=read(),s=read(),k=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=m;i++) b[i].x=read(),b[i].y=read(); sort(b+1,b+m+1); m=unique(b+1,b+m+1)-b-1; for (int i=1;i<=m;i++) for (int j=1;j<=m;j++) if (i!=j&&b[j].x<=b[i].x&&b[j].y>=b[i].y) {flag[i]=1;break;} int t=0; for (int i=1;i<=m;i++) if (!flag[i]) d[++t]=b[i]; m=t;s=min(s,m); for (int i=1;i<=m;i++) b[i]=d[i]; sort(b+1,b+m+1); int l=1,r=1000000000; while (l<=r) { int mid=l+r>>1; if (check(mid)) ans=mid,r=mid-1; else l=mid+1; } cout<<ans; return 0; //NOTICE LONG LONG!!!!! }
F:冷静一下可以发现,由异或的消去性,这个题和树没有任何关系,求出每个点到根的路径的异或和后,就变为一堆数的问题。容易想到的做法是建一棵trie,二分答案,对每个数trie上跑一遍。时间复杂度O(nlog2v),空间复杂度O(nlogv),都炸掉了。
先考虑优化时间复杂度。trie还是要建的,改为按位考虑。对于当前位是否填1,记录所有需要考虑的对(即异或后与已确定位完全相同),算出下一位均填0和均填1的方案数(即异或的下一位填0),与k进行比较确定答案,如果填1就将k减掉该方案数。然后给每个对拓展下一位。注意到考虑第i位时,所有对都处于trie的第i层,并且每个点只会被考虑两次,所以时间复杂度O(nlogv)。
然后考虑将空间复杂度优化至线性。将所有数排序,就得到了所谓的隐式trie,因为排序后的一段区间就对应了trie上的一棵子树。跑同样的做法即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; #define ll long long #define N 1000010 ll read() { ll x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,p[N],t,cnt,tot; ll m,ans,a[N],b[N][2],c[N][2]; struct data{int to,nxt;ll len; }edge[N]; void addedge(int x,int y,ll z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;} void dfs(int k) { for (int i=p[k];i;i=edge[i].nxt) { a[edge[i].to]=a[k]^edge[i].len; dfs(edge[i].to); } } int jump(int x,int u,int k) { if (u==0) return ((a[x]>>k)&1)==0?x:-1; for (int i=x;i<=n;i++) { if ((a[i]>>k+1)!=(a[x]>>k+1)) return -1; if ((a[i]>>k)&1) return i; } return -1; } int query(int x,int u,int k) { x=jump(x,u,k); if (x==-1) return 0; for (int i=x+1;i<=n;i++) if ((a[i]>>k)!=(a[x]>>k)) return i-x; return n-x+1; } signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #endif n=read(),m=read(); for (int i=2;i<=n;i++) { int x=read();ll y=read(); addedge(x,i,y); } dfs(1);sort(a+1,a+n+1);cnt++;b[cnt][0]=b[cnt][1]=1; for (int k=62;~k;k--) { ll s=0; for (int i=1;i<=cnt;i++) s+=1ll*query(b[i][0],0,k)*query(b[i][1],0,k)+1ll*query(b[i][0],1,k)*query(b[i][1],1,k); tot=0;ans<<=1; if (s>=m) { for (int i=1;i<=cnt;i++) { if (query(b[i][0],0,k)&&query(b[i][1],0,k)) tot++,c[tot][0]=jump(b[i][0],0,k),c[tot][1]=jump(b[i][1],0,k); if (query(b[i][0],1,k)&&query(b[i][1],1,k)) tot++,c[tot][0]=jump(b[i][0],1,k),c[tot][1]=jump(b[i][1],1,k); } } else { for (int i=1;i<=cnt;i++) { if (query(b[i][0],0,k)&&query(b[i][1],1,k)) tot++,c[tot][0]=jump(b[i][0],0,k),c[tot][1]=jump(b[i][1],1,k); if (query(b[i][0],1,k)&&query(b[i][1],0,k)) tot++,c[tot][0]=jump(b[i][0],1,k),c[tot][1]=jump(b[i][1],0,k); } m-=s;ans++; } cnt=tot; for (int i=1;i<=cnt;i++) b[i][0]=c[i][0],b[i][1]=c[i][1]; } cout<<ans; return 0; //NOTICE LONG LONG!!!!! }
G:先考虑怎么求答案是否为0。平面图上下连通相当于对偶图左右不连通,考虑以凸多边形某个点标记Bob,那么每个圆会给该点带来一个形如圆角凸多边形的不合法区域,不合法区域有交则称两圆连通,如果这些不合法区域使得左右连通,说明答案>0。回到原题,做法类似,对圆之间的连通关系(及圆和边界的连通关系)建图后,即求最小割点数量,拆点跑最小割即可。
主要问题在于如何判断圆角凸多边形是否有交。真要带上圆角简直不可做,事实上可以转化为求两凸多边形的最近距离。对任一凸包取反后求闵可夫斯基和,看所得凸包中是否存在点到原点的距离<两圆半径之和即可。这得讨论一下原点是否在凸包中。然后我就因为这个写挂调了几天。自闭了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<cassert> using namespace std; #define ll long long #define int long long #define N 410 #define S 0 #define T 401 #define in(i) (i*2-1) #define out(i) (i*2) #define vector point #define p(x,y) (point){x,y} #define O p(0,0) int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,w,L,R,p[N],d[N],q[N],cur[N],t=-1,ans; struct point { int x,y; vector operator +(const vector&a) const { return (vector){x+a.x,y+a.y}; } vector operator -(const vector&a) const { return (vector){x-a.x,y-a.y}; } int operator *(const vector&a) const { return x*a.y-y*a.x; } int operator ^(const vector&a) const { return x*a.x+y*a.y; } }a[N],u[N<<1],v[N<<1],r[N<<1],Left; struct circle{point o;int r; }b[N]; struct data{int to,nxt,cap,flow; }edge[N*N<<3]; void addedge(int x,int y,int z) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,p[y]=t; } bool bfs() { memset(d,255,sizeof(d));d[S]=0; int head=0,tail=1;q[1]=S; do { int x=q[++head]; for (int i=p[x];~i;i=edge[i].nxt) if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap) { d[edge[i].to]=d[x]+1; q[++tail]=edge[i].to; } }while (head<tail); return ~d[T]; } int work(int k,int f) { if (k==T) return f; int used=0; for (int i=cur[k];~i;i=edge[i].nxt) if (d[k]+1==d[edge[i].to]) { int w=work(edge[i].to,min(f-used,edge[i].cap-edge[i].flow)); edge[i].flow+=w,edge[i^1].flow-=w; if (edge[i].flow<edge[i].cap) cur[k]=i; used+=w;if (used==f) return f; } if (used==0) d[k]=-1; return used; } void dinic() { while (bfs()) { memcpy(cur,p,sizeof(p)); ans+=work(S,N); } } int len(vector a){return a^a;} bool isin(point *r,int n) { for (int i=2;i<=n;i++) if ((r[i]-r[i-1])*(O-r[i-1])<0) return 0; return 1; } bool check(point a,point b,int r) { if (((b-a)^(O-a))>0&&((a-b)^(O-b))>0) return ((b-a)*(O-a))*((b-a)*(O-a))<r*r*len(a-b); else return 0; } int merge(point *u,point *v) { r[1]=u[1]+v[1];int tail=1; int i=1,j=1; while (i<=n||j<=n) { if (i>n) j++; else if (j>n) i++; else if ((u[i]+v[j+1]-r[tail])*(u[i+1]+v[j]-r[tail])>0) j++; else i++; point t=u[i]+v[j]; while (tail>1&&(t-r[tail-1])*(r[tail]-r[tail-1])>0) tail--; r[++tail]=t; } return tail; } bool cross(circle x,circle y) { for (int i=1;i<=n;i++) u[i]=x.o-(a[i]-Left); for (int i=1;i<=n;i++) v[i]=(a[i]-Left)-y.o; int u_first=1; for (int i=2;i<=n;i++) if (u[i].x<u[u_first].x||u[i].x==u[u_first].x&&u[i].y<u[u_first].y) u_first=i; int v_first=1; for (int i=2;i<=n;i++) if (v[i].x<v[v_first].x||v[i].x==v[v_first].x&&v[i].y<v[v_first].y) v_first=i; for (int i=n+1;i<=2*n;i++) u[i]=u[i-n],v[i]=v[i-n]; int tail=merge(u+u_first-1,v+v_first-1); if (isin(r,tail)) return 1; for (int i=1;i<=tail;i++) if (len(r[i])<(x.r+y.r)*(x.r+y.r)) return 1; for (int i=1;i<tail;i++) if (check(r[i],r[i+1],x.r+y.r)) return 1; return 0; } signed main() { #ifndef ONLINE_JUDGE freopen("g.in","r",stdin); freopen("g.out","w",stdout); const char LL[]="%I64d "; #endif n=read(),w=read();memset(p,255,sizeof(p)); Left=(point){N*N,N*N};L=N*N;R=-N*N; for (int i=1;i<=n;i++) { a[i].x=read(),a[i].y=read(); L=min(L,a[i].x),R=max(R,a[i].x); if (a[i].x<Left.x) Left=a[i]; } m=read(); for (int i=1;i<=m;i++) b[i].o.x=read(),b[i].o.y=read(),b[i].r=read(); for (int i=1;i<=m;i++) for (int j=i+1;j<=m;j++) if (cross(b[i],b[j])) addedge(out(i),in(j),1),addedge(out(j),in(i),1); for (int i=1;i<=m;i++) { if (b[i].o.x-(R-L)-b[i].r<0) addedge(S,in(i),1); if (b[i].o.x+(R-L)+b[i].r>w) addedge(out(i),T,1); } for (int i=1;i<=m;i++) addedge(in(i),out(i),1); dinic(); cout<<ans; return 0; //NOTICE LONG LONG!!!!! }