A:签到。
#include<bits/stdc++.h> 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 n;char s[100]; signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif scanf("%s",s+1);n=strlen(s+1);int cnt=0; for (int i=1;i<=n;i++) if (s[i]=='a') cnt++; for (int i=n;i>=1;i--) if (cnt>i/2) {cout<<i;break;} return 0; //NOTICE LONG LONG!!!!! }
B:显然可能的划分位置只有一个,找到后检验即可。
#include<bits/stdc++.h> 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;char s[N],a[N],b[N]; signed main() { scanf("%s",s+1);n=strlen(s+1);int cnt=0; int cnt2=0;for (int i=1;i<=n;i++) if (s[i]=='a') cnt2++; for (int i=1;i<=n;i++) { if (s[i]=='a') cnt++; if (cnt==cnt2&&i-cnt==n-i) { int t=0; for (int j=1;j<=i;j++) if (s[j]!='a') a[++t]=s[j]; t=0; for (int j=i+1;j<=n;j++) b[++t]=s[j]; for (int j=1;j<=t;j++) if (a[j]!=b[j]) {cout<<":(";return 0;} for (int j=1;j<=i;j++) putchar(s[j]);return 0; } } cout<<":("; return 0; //NOTICE LONG LONG!!!!! }
C:二进制分组。
#include<bits/stdc++.h> 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 T,n,a[110],b[110]; signed main() { T=read(); while (T--) { n=read(); int ans=0; for (int i=7;i>=0;i--) { int u=0,v=0; for (int j=1;j<=n;j++) if (j&(1<<i)) a[++u]=j; else b[++v]=j; if (u&&v) { cout<<u<<' '<<v<<' '; for (int j=1;j<=u;j++) cout<<a[j]<<' '; for (int j=1;j<=v;j++) cout<<b[j]<<' '; cout<<endl; ans=max(ans,read()); } } cout<<-1<<' '<<ans<<endl; } return 0; //NOTICE LONG LONG!!!!! }
D:不考虑范围限制的话显然能到达所有gcd(a,b)的倍数。注意到对于不小于a+b的gcd(a,b)的倍数,该点一定可以为到达该点时所经过的最大值。因为可以考虑每次若能往左走就往左走,否则往右走,显然这样往右走的时候所达位置一定<=a+b,且这样可以到达0~a中所有gcd(a,b)的倍数,由这些点一直向右就可以到达所有gcd(a,b)的倍数了。那么对于<a+b的数暴力跑dij,>=a+b的数等差数列求和即可。然后因为inf设小调了一年。
#include<bits/stdc++.h> using namespace std; #define ll long long #define M 1000000 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,a,b,d[1000010]; bool flag[1000010]; struct data { int x,d; bool operator <(const data&a) const { return d>a.d; } }; priority_queue<data> q; void dij() { for (int i=1;i<=M;i++) d[i]=1000000000;d[0]=0; q.push((data){0,0}); for (;;) { while (!q.empty()&&flag[q.top().x]) q.pop(); if (q.empty()) break; data x=q.top();q.pop(); flag[x.x]=1; if (x.x>=b&&d[x.x-b]>x.d) { d[x.x-b]=x.d; q.push((data){x.x-b,d[x.x-b]}); } if (x.x+a<=M&&d[x.x+a]>max(x.x+a,x.d)) { d[x.x+a]=max(x.x+a,x.d); q.push((data){x.x+a,d[x.x+a]}); } } } signed main() { n=read(),a=read(),b=read(); dij(); ll ans=0; for (int i=0;i<=min(n,M);i++) ans+=max(0,n-d[i]+1); int u=gcd(a,b),cnt=n/u-M/u; if (cnt>0) { ans+=1ll*cnt*(n+1); int l,r; for (l=M+1;l<=n;l++) if (l%u==0) break; for (r=n;r>M;r--) if (r%u==0) break; ans-=1ll*(l+r)*cnt/2; } //��i (i=1000001~n,i%u==0) cout<<ans; return 0; //NOTICE LONG LONG!!!!! }
E:考虑权值线段树维护每个数当前的符号情况。<x和>x类似,不妨只讨论<x。若x不为正数,相当于把最初<x和>-x的数强制变成正数,是区间覆盖。若x为正数,相当于把最初>=x和<=-x的数强制变成正数,并且将最初在(x,-x)的数符号取反,区间覆盖+区间取反。
#include<bits/stdc++.h> using namespace std; #define ll long long #define inf 1000000010 #define N 200010 #define V 100001 char getc(){char c=getchar();while (c!='<'&&c!='>') 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],tree[N<<2],tag[N<<2]; void build(int k,int l,int r) { if (l==r) {tree[k]=1;return;} int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void down(int k) { if (tree[k]) tree[k<<1]=tree[k<<1|1]=tree[k],tag[k<<1]=tag[k<<1|1]=0; else { if (tree[k<<1]) tree[k<<1]=-tree[k<<1];else tag[k<<1]^=1; if (tree[k<<1|1]) tree[k<<1|1]=-tree[k<<1|1];else tag[k<<1|1]^=1; } tree[k]=tag[k]=0; } void cover(int k,int l,int r,int x,int y,int p) { if (l==x&&r==y) {tree[k]=p;tag[k]=0;return;} if (tree[k]||tag[k]) down(k); int mid=l+r>>1; if (y<=mid) cover(k<<1,l,mid,x,y,p); else if (x>mid) cover(k<<1|1,mid+1,r,x,y,p); else cover(k<<1,l,mid,x,mid,p),cover(k<<1|1,mid+1,r,mid+1,y,p); } int query(int k,int l,int r,int x) { if (l==r) return tree[k]; if (tree[k]||tag[k]) down(k); int mid=l+r>>1; if (x<=mid) return query(k<<1,l,mid,x); else return query(k<<1|1,mid+1,r,x); } void rev(int k,int l,int r,int x,int y) { if (l==x&&r==y) {if (tree[k]) tree[k]=-tree[k];else tag[k]^=1;return;} if (tree[k]||tag[k]) down(k); int mid=l+r>>1; if (y<=mid) rev(k<<1,l,mid,x,y); else if (x>mid) rev(k<<1|1,mid+1,r,x,y); else rev(k<<1,l,mid,x,mid),rev(k<<1|1,mid+1,r,mid+1,y); } void Cover(int l,int r,int x){cover(1,1,2*V+1,l+V+1,r+V+1,x);} int Query(int x){return query(1,1,2*V+1,x+V+1);} void Rev(int l,int r){rev(1,1,2*V+1,l+V+1,r+V+1);} signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); build(1,1,2*V+1); while (m--) { char c=getc();int x=read(); if (c=='<') { if (x<=0) Cover(-V,x-1,-1),Cover(-x+1,V,1); else Cover(-V,-x,-1),Cover(x,V,1),Rev(-x+1,x-1); } else { if (x>=0) Cover(-V,-x-1,1),Cover(x+1,V,-1); else Cover(-V,x,1),Cover(-x,V,-1),Rev(x+1,-x-1); } // for (int i=1;i<=n;i++) printf("%d ",Query(a[i])*a[i]);cout<<endl; } for (int i=1;i<=n;i++) printf("%d ",Query(a[i])*a[i]); return 0; //NOTICE LONG LONG!!!!! }
F:这种看上去特别弱智但特别容易假的题好烦啊。就不写心路历程了因为我也不知道怎么假着假着就真了。
对于当前考虑的子树,显然是在儿子子树中选一些集合并将根加入,但是如果根在其中只属于某一个儿子子树的集合,不一定构成合法方案。于是就来乱七八糟的dp。
设f[i][0/1/2]为i子树内根被强制分配(即只属于某个儿子子树的集合)的方案数/被非强制分配(即属于多个儿子子树的集合)的方案数/i子树的答案。考虑转移。
显然f[i][2]是可以由f[i][1]得到的,相当于是选择一些点,使其相互不包含,且其是子树内所有叶子的lca,且其覆盖了所有叶子,这些点f[i][1]的乘积,所有选择点的方案的乘积之和。有点绕但意义很好理解。
然后考虑f[i][0]的转移。考虑将根分配进哪个儿子子树的集合,此时该儿子子树的方案数应该是f[son][0]+f[son][1],而其他儿子子树的方案数是f[son][2]。求逆元(似乎不太会出现0?反正过掉了)或者求前后缀积即可。
最后是f[i][1]。直接算不太行,考虑补集转化,不管根是否是被强制拉进来的话方案数就是∏((f[son][0]+f[son][1])+f[son][2]),意义与f[i][0]时的类似,前一项是在包含根的集合内,后一项不是。然后去掉不合法方案,可以发现是f[i][0]+∏f[son][2],∏f[son][2]对应根不在任意一个集合,f[i][0]对应根只在一个集合。
#include<bits/stdc++.h> using namespace std; #define ll long long #define N 200010 #define P 998244353 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,fa[N],p[N],f[N][3],t; struct data{int to,nxt; }edge[N]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} int ksm(int a,int k) { int s=1; for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P; return s; } int inv(int a){return ksm(a,P-2);} void dfs(int k) { int s=0;for (int i=p[k];i;i=edge[i].nxt) s++; f[k][1]=1;f[k][2]=1; for (int i=p[k];i;i=edge[i].nxt) { dfs(edge[i].to); f[k][1]=1ll*f[k][1]*((f[edge[i].to][0]+f[edge[i].to][1])%P+f[edge[i].to][2])%P; f[k][2]=1ll*f[k][2]*f[edge[i].to][2]%P; } if (s) { for (int i=p[k];i;i=edge[i].nxt) { int x=1ll*inv(f[edge[i].to][2])*f[k][2]%P*(f[edge[i].to][0]+f[edge[i].to][1])%P; f[k][0]=(f[k][0]+x)%P; } f[k][1]=(f[k][1]-f[k][0]+P)%P; f[k][1]=(f[k][1]-f[k][2]+P)%P; } else f[k][0]=0,f[k][1]=1,f[k][2]=0; f[k][2]=(f[k][2]+f[k][1])%P; } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif n=read(); for (int i=2;i<=n;i++) fa[i]=read(),addedge(fa[i],i); dfs(1);//for (int i=1;i<=n;i++) cout<<f[i][0]<<' '<<f[i][1]<<' '<<f[i][2]<<endl; cout<<f[1][2]; return 0; }
G:只要做过切糕并且知道这题可以网络流,很容易就能想到做法。考虑最小割,每个位置建一条链的点表示高度,割掉链上某条边就表示选择该高度,其中边权为h2-h'2。对于每个限制建一个点,将限制区间内所有点的对应高度向该点连inf边,该点向汇连边权为代价的边,直接跑最小割即可。
#include<bits/stdc++.h> using namespace std; #define ll long long #define N 55 #define S 0 #define T 3000 #define inf N*N*N char getc(){char c=getchar();while (c!='<'&&c!='>') 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,h,p[N*N],d[N*N],cur[N*N],q[N*N],id[N][N],cnt,t=-1,ans; struct data{int to,nxt,cap,flow; }edge[N*N<<2]; void addedge(int x,int y,int z) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=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,inf); } } signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(),h=read(),m=read(); memset(p,255,sizeof(p)); for (int i=1;i<=n;i++) for (int j=1;j<=h;j++) id[i][j]=++cnt; for (int i=1;i<=n;i++) { addedge(S,id[i][1],h*h); for (int j=1;j<h;j++) addedge(id[i][j],id[i][j+1],h*h-j*j); } for (int i=1;i<=m;i++) { int l=read(),r=read(),x=read(),c=read(); if (x<h) { cnt++; for (int j=l;j<=r;j++) addedge(id[j][x+1],cnt,inf); addedge(cnt,T,c); } } dinic(); cout<<n*h*h-ans; return 0; //NOTICE LONG LONG!!!!! }
或者可以区间dp,设f[i][j][k]为i~j区间最大值不超过k时,只考虑完全被该区间包含的限制,最大价值是多少。转移枚举最高点所在位置,计算跨过该点的代价,然后两边就可以被划分为子问题。
#include<bits/stdc++.h> using namespace std; #define ll long long #define N 55 #define S 0 #define T 3000 char getc(){char c=getchar();while (c!='<'&&c!='>') 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,h,f[N][N][N]; struct data{int l,r,x,c; }lim[N]; signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(),h=read(),m=read(); for (int i=1;i<=m;i++) lim[i].l=read(),lim[i].r=read(),lim[i].x=read(),lim[i].c=read(); for (int k=1;k<=n;k++) for (int l=1;l<=n-k+1;l++) { int r=l+k-1; for (int x=0;x<=h;x++) for (int d=l;d<=r;d++) { int tot=x*x; for (int _=1;_<=m;_++) if (lim[_].l>=l&&lim[_].r<=r&&lim[_].l<=d&&lim[_].r>=d&&lim[_].x<x) tot-=lim[_].c; f[l][r][x]=max(f[l][r][x],tot+f[l][d-1][x]+f[d+1][r][x]); } for (int x=1;x<=h;x++) f[l][r][x]=max(f[l][r][x],f[l][r][x-1]); } cout<<f[1][n][h]; return 0; //NOTICE LONG LONG!!!!! }
H:显然相当于求五个点的凸包数量。考虑枚举上顶点,将其他点按极角排序。要满足是凸包,只需要相邻两点所成射线的斜率依次递增(这里取atan2值作为斜率)即可。设凸包上的点逆时针排列依次为abcde,其中a为上顶点。对于每个点,求出其作为c时所有能与其构成凸包的b,将其构成的射线按斜率排序。同理求出其作为d时的所有e。然后枚举cd,根据cd斜率在两个已排序的斜率数组中二分即可。我觉得比D简单
#include<bits/stdc++.h> using namespace std; #define ll long long #define inf 1000000010 #define N 310 #define vector point 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,down_cnt[N],up_cnt[N]; ll ans; double down[N][N],up[N][N]; 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}; } ll operator *(const vector&a) const { return 1ll*x*a.y-1ll*y*a.x; } }a[N],b[N],O; double alpha(point x) { return atan2(x.y,x.x); } bool cmp1(const point&a,const point&b) { return a.y>b.y; } bool cmp2(const point&a,const point&b) { return alpha(a)<alpha(b); } bool onright(point a,point b,point c) { return (c-a)*(b-a)>0; } signed main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(); for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(); sort(a+1,a+n+1,cmp1); for (int i=1;i<=n;i++) { m=0; for (int j=i+1;j<=n;j++) b[++m]=a[j]-a[i]; sort(b+1,b+m+1,cmp2); for (int j=1;j<=m;j++) { down_cnt[j]=0; for (int k=1;k<j;k++) if (onright(O,b[j],b[k])) down[j][++down_cnt[j]]=alpha(b[j]-b[k]); sort(down[j]+1,down[j]+down_cnt[j]+1); up_cnt[j]=0; for (int k=j+1;k<=m;k++) if (onright(O,b[k],b[j])) up[j][++up_cnt[j]]=alpha(b[k]-b[j]); sort(up[j]+1,up[j]+up_cnt[j]+1); } for (int j=1;j<=m;j++) for (int k=j+1;k<=m;k++) { int x=lower_bound(down[j]+1,down[j]+down_cnt[j]+1,alpha(b[k]-b[j]))-down[j]-1; int y=up_cnt[k]-(lower_bound(up[k]+1,up[k]+up_cnt[k]+1,alpha(b[k]-b[j]))-up[k])+1; //cout<<a[i].x<<' '<<a[i].y<<" "<<b[j].x<<' '<<b[j].y<<" "<<b[k].x<<' '<<b[k].y<<" "<<x<<' '<<y<<endl; ans+=1ll*x*y; } } cout<<ans; return 0; //NOTICE LONG LONG!!!!! }