2019牛客暑期多校训练营(第二场)
A:
这是一道逗鹅题
队友一上来猜结论 猜对了
但是wa了
然后就扔了这题了
最后我暴力发现队友的结论没错
然后鹅发现没累乘
D:
找团好像没什么快的方法
只能是暴力
这个题虽然团有很多很多个
但是我们只找前k小的就行
现在的问题在于我们不能按照从小到大依次访问到每个团
第一个方法是用优先队列
我们一开始把单个的点扔进去
每次弹出来一个权值最小的团
我们虽然不能保证队列里的前k个就是当前的前k小
但是我们能保证现在队首的是当前最小的
因为后面在队列里的状态一定比他大
还没访问的到状态是由队列里的转移过去的 会更大
所以每次弹出队首一定是正确的
然后对于队首的状态 他能到达的状态拿到队列里
这样子 只需要k次弹出就结束了
每次弹出然后拓展->O(logn+n*X)
X这里暴力的话是n,就是循环,会tle
然后用int128或者bitset优化一下就好了
第二个方法是二分
回到前面的问题
我们不能依次从小到大遍历所有的团
但是如果我们知道一个lim
我们现在要找权值<=lim的团们
这样我们就可以暴力的搜索
如果拓展到一个合法的团 那就继续
这样的操作会使得k--
如果扩展到一个不合法的 就停止
这样只会浪费一次 不会在非法的道路上走得很远
而这个lim又有单调性 就可以二分
O(log MAX_lim * k * X)
这个X是每次找到一个新的最大团时间
可以用int128优化
#include<cstdio> #include<cstring> #include<iostream> #include<queue> #define maxn 110 #define tpc __int128 #define ll long long using namespace std; ll n,k,w[maxn]; tpc G[maxn]; struct node{ ll big,val; tpc S; bool operator < (const node &A )const{ return A.val<val; } }; priority_queue<node>q; char s[maxn][maxn]; int main(){ scanf("%lld%lld",&n,&k); k--;node now,tmp; for(ll i=1;i<=n;i++) scanf("%lld",&w[i]); for(ll i=1;i<=n;i++) scanf("%s",s[i]+1); for(ll i=1;i<=n;i++){ for(ll j=1;j<=n;j++) if(s[i][j]=='1')G[i]+=tpc(1)<<j; now.val=w[i];now.S=tpc(1)<<i; now.big=i;q.push(now); } now.val=0; for(ll i=1;i<=k;i++){ if(q.empty()){ now.val=-1;break; } now=q.top();q.pop(); for(ll v=now.big+1;v<=n;v++){ if((G[v]&now.S)==now.S){ tmp.big=v;tmp.val=now.val+w[v]; tmp.S=now.S|(tpc(1)<<v);q.push(tmp); } } } printf("%lld\n",now.val); return 0; }
#include<cstdio> #include<cstring> #include<iostream> #include<queue> #include<bitset> #include<algorithm> #define tpc int #define maxn 110 #define ll long long using namespace std; int n,k,cnt; tpc G[maxn],S; ll ans,l,r; struct node{ int w,o; }a[maxn]; char s[maxn][maxn]; int cmp(const node &A,const node &B){ return A.w<B.w; } void Dfs(int now,ll val,ll lim){ int id=a[now].o; if(now==n+1||cnt>=k)return; if(val+a[now].w>lim)return; if((G[id]&S)==S){ cnt++;S|=tpc(1)<<id; Dfs(now+1,val+a[now].w,lim); S^=tpc(1)<<id; } Dfs(now+1,val,lim); } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ scanf("%d",&a[i].w);a[i].o=i;r+=a[i].w; } sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) if(s[i][j]=='1')G[i]+=tpc(1)<<j; } if(k<=1){ printf("0\n");return 0; } k--;ans=-1; while(l<=r){ ll mid=(l+r)/2; cnt=0;S=0;Dfs(1,0,mid); if(cnt>=k){ ans=mid;r=mid-1; } else l=mid+1; } printf("%lld\n",ans); return 0; } /* 3 6 3 2 1 011 101 110 5 22 1 2 3 4 5 01111 10111 11011 11101 11110 5 22 5 4 3 2 1 01111 10111 11011 11101 11110 */
E:
好奇妙的做法
线段树维护l行的x到r行的y的方案数,两个区间合并就是矩阵乘法
啊不要把m写成n啊,要不T的怀疑人生
#include<cstdio> #include<cstring> #include<iostream> #define maxn 50010 #define lc k<<1 #define rc k<<1|1 #define mid (l+r)/2 #define mod 1000000007 #define ll long long using namespace std; int n,m,Q,g[maxn][11],s[maxn*4][11][11]; char c[11]; void Build(int k,int l,int r){ if(l==r){ for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) s[k][i][j]=0; for(int i=1;i<=m;i++){ for(int j=i;j>=1;j--) if(g[l][j]==0)s[k][i][j]=1; else break; } for(int i=1;i<=m;i++){ for(int j=i;j<=m;j++) if(g[l][j]==0)s[k][i][j]=1; else break; } return; } Build(lc,l,mid);Build(rc,mid+1,r); for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) s[k][i][j]=0; for(int i=1;i<=m;i++) for(int t=1;t<=m;t++){ if(s[lc][i][t]==0)continue; for(int j=1;j<=m;j++) s[k][i][j]=(s[k][i][j]+1ll*s[lc][i][t]*s[rc][t][j])%mod; } } void Insert(int k,int l,int r,int x){ if(l==r){ for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) s[k][i][j]=0; for(int i=1;i<=m;i++){ for(int j=i;j>=1;j--) if(g[l][j]==0)s[k][i][j]=1; else break; } for(int i=1;i<=m;i++){ for(int j=i;j<=m;j++) if(g[l][j]==0)s[k][i][j]=1; else break; } return; } if(x<=mid)Insert(lc,l,mid,x); else Insert(rc,mid+1,r,x); for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) s[k][i][j]=0; for(int i=1;i<=m;i++) for(int t=1;t<=m;t++){ if(s[lc][i][t]==0)continue; for(int j=1;j<=m;j++) s[k][i][j]=(s[k][i][j]+1ll*s[lc][i][t]*s[rc][t][j])%mod; } } int main(){ scanf("%d%d%d",&n,&m,&Q); for(int i=1;i<=n;i++){ scanf("%s",c+1); for(int j=1;j<=m;j++) g[i][j]=c[j]-'0'; } Build(1,1,n);int x,y,z; while(Q--){ scanf("%d%d%d",&x,&y,&z); if(x==1){ g[y][z]^=1;Insert(1,1,n,y); } else printf("%d\n",s[1][y][z]); } return 0; } /* 2 2 10 00 00 2 1 2 1 1 2 2 1 2 2 1 2 1 1 2 2 1 2 2 1 2 1 1 2 2 1 2 2 1 2 */
F:
暴力+最优化剪枝
ans=所有对之前的val和-队伍内部之间互相打架的val
后面这个val是递增的,就可以用最优化剪枝
#include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #define maxn 30 #define ll long long using namespace std; int n,g[maxn][maxn],A[maxn],B[maxn]; ll ans,sum; void Dfs(int now,ll s,int a,int b){ ll res; if(now==2*n+1){ if(ans<sum-s){ ans=sum-s;//printf("ans : %d\n",ans); //for(int i=1;i<=n*2;i++)printf("%d ",S[i]);printf("\n"); } return; } if(a<b){ res=0; for(int i=1;i<=a;i++) res+=g[now][A[i]]; if(sum-s-res>ans&&a<n){ A[a+1]=now;Dfs(now+1,s+res,a+1,b);A[a+1]=0; } res=0; for(int i=1;i<=b;i++) res+=g[now][B[i]]; if(sum-s-res>ans&&b<n){ B[b+1]=now;Dfs(now+1,s+res,a,b+1);B[b+1]=0; } } else { res=0; for(int i=1;i<=b;i++) res+=g[now][B[i]]; if(sum-s-res>ans&&b<n){ B[b+1]=now;Dfs(now+1,s+res,a,b+1);B[b+1]=0; } res=0; for(int i=1;i<=a;i++) res+=g[now][A[i]]; if(sum-s-res>ans&&a<n){ A[a+1]=now;Dfs(now+1,s+res,a+1,b);A[a+1]=0; } } } int main(){ scanf("%d",&n); for(int i=1;i<=n*2;i++) for(int j=1;j<=n*2;j++){ scanf("%d",&g[i][j]);sum+=g[i][j]; } sum/=2;Dfs(1,0,0,0);printf("%lld\n",ans); return 0; } /* 2 0 1 1000000000 1000000000 1 0 1000000000 1000000000 1000000000 1000000000 0 1 1000000000 1000000000 1 0 */
H:
枚举删除最大1矩阵的那个边
#include<iostream> #include<cstdio> #include<cstring> #define maxn 1010 using namespace std; int n,m,h[maxn][maxn],l[maxn][maxn],r[maxn][maxn],ans,x1,y1,x2,y2; char g[maxn][maxn],t[maxn]; int Solve(int falg){ memset(h,0,sizeof(h)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); int res=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ l[i][j]=r[i][j]=j; if(g[i][j]=='0')continue; else h[i][j]=h[i-1][j]+1; } // printf("h:\n"); // for(int i=1;i<=n;i++){ // for(int j=1;j<=m;j++) // printf("%d ",h[i][j]); // printf("\n"); // } for(int i=1;i<=n;i++){ for(int j=2;j<=m;j++){ if(g[i][j]=='0')continue; while(h[i][l[i][j]-1]>=h[i][j]) l[i][j]=l[i][l[i][j]-1]; } } for(int i=1;i<=n;i++){ for(int j=m-1;j>=1;j--){ if(g[i][j]=='0')continue; while(h[i][r[i][j]+1]>=h[i][j]) r[i][j]=r[i][r[i][j]+1]; } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(res<h[i][j]*(r[i][j]-l[i][j]+1)){ res=h[i][j]*(r[i][j]-l[i][j]+1); if(falg){ x1=i-h[i][j]+1;y1=l[i][j];x2=i;y2=r[i][j]; } } } return res; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",g[i]+1); Solve(1); for(int i=y1;i<=y2;i++){ t[i]=g[x1][i];g[x1][i]='0'; } ans=max(ans,Solve(0)); for(int i=y1;i<=y2;i++){ g[x1][i]=t[i]; } for(int i=y1;i<=y2;i++){ t[i]=g[x2][i];g[x2][i]='0'; } ans=max(ans,Solve(0)); for(int i=y1;i<=y2;i++){ g[x2][i]=t[i]; } for(int i=x1;i<=x2;i++){ t[i]=g[i][y1];g[i][y1]='0'; } ans=max(ans,Solve(0)); for(int i=x1;i<=x2;i++){ g[i][y1]=t[i]; } for(int i=x1;i<=x2;i++){ t[i]=g[i][y2];g[i][y2]='0'; } ans=max(ans,Solve(0)); for(int i=x1;i<=x2;i++){ g[i][y2]=t[i]; } printf("%d\n",ans); //printf("%d %d %d %d\n",x1,y1,x2,y2); return 0; } /* 5 6 001010 000110 010111 010111 000110 4 5 11011 01010 10110 00111 4 9 001110011 001110011 001110011 000000011 */
G:
蓝书上的板子
求出所有的多边形面积
#include<bits/stdc++.h> #define db double #define Vector Point using namespace std; struct Point{ db x,y; Point(db X=0,db Y=0){x=X;y=Y;} }; Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);} Vector operator - (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);} Vector operator * (Vector A,db p){return Vector(A.x*p,A.y*p);} Vector operator / (Vector A,db p){return Vector(A.x/p,A.y/p);} bool operator < (const Point &a,const Point &b){ return a.x<b.x||(a.x==b.x&&a.y<b.y); } const db eps=1e-10; int dcmp(db x){ if(fabs(x)<eps)return 0; else return x<0?-1:1; } bool operator ==(const Point &a,const Point &b){ return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0; } db Dot(Vector A,Vector B){return A.x*B.x+A.y*B.y;}//A B 点积 db Length(Vector A){return sqrt(Dot(A,A));}//向量长度 db Angle(Vector A,Vector B){return acos(Dot(A,B)/Length(A)/Length(B));}//A B 夹角 db Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}//A B 叉积 db Area2(Point A,Point B,Point C){return Cross(B-A,C-A);}//A B 构成的平行四边形面积2倍 Vector Rotate(Vector A,db rad){//A逆时针旋转rad return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad)); } Vector Normal(Vector A){//返回A的单位法向量 return Vector(-A.y/Length(A),A.x/Length(A)); } //点和直线 Point GetLineIntersection(Point P,Vector v,Point Q,Vector w){//返回直线P+tv Q+tw的交点 Vector u=P-Q; db t=Cross(w,u)/Cross(v,w); return P+v*t; } db DistanceToLine(Point P,Point A,Point B){//P到直线AB的距离 Vector v1=B-A,v2=P-A; return fabs(Cross(v1,v2)/Length(v1)); } db DistanceToSegment(Point P,Point A,Point B){//P到线段AB的距离 if(A==B)return Length(P-A); Vector v1=B-A,v2=P-A,v3=P-B; if(dcmp(Dot(v1,v2))<0)return Length(v2); else if(dcmp(Dot(v1,v3))>0)return Length(v3); else return fabs(Cross(v1,v2))/Length(v1); } Point GetLineProjection(Point P,Point A,Point B){//P在直线AB上的投影 Vector v=B-A; return A+v*(Dot(v,P-A)/Dot(v,v)); } //bool isSL(Point p1,Point p2,Point q1,Point q2){//判断直线p1p2和线段q1q2有无交点(不严格) // return dcmp(Cross(p2-p1,q1-p1)*Cross(p2-p1,q2-p1))!=1; //} //bool isSL_s(Point p1,Point p2,Point q1,Point q2){//判断直线p1p2和线段q1q2有无交点(严格) // return dcmp(Cross(p2-p1,q1-p1)*Cross(p2-p1,q2-p1))==-1; //} int isLL(Point k1,Point k2,Point k3,Point k4){// 求直线 k1,k2 和 k3,k4 的交点 return dcmp(Cross(k3-k1,k4-k1)-Cross(k3-k2,k4-k2))!=0; } Point getLL(Point k1,Point k2,Point k3,Point k4){ db w1=Cross(k1-k3,k4-k3),w2=Cross(k4-k3,k2-k3); return (k1*w2+k2*w1)/(w1+w2); } bool intersect(db l1,db r1,db l2,db r2){ if(dcmp(l1-r1)==1)swap(l1,r1);if(dcmp(l2-r2)==1)swap(l2,r2); return !(dcmp(r1-l2)==-1||dcmp(r2-l1)==-1); } bool isSS(Point p1,Point p2,Point q1,Point q2){ return intersect(p1.x,p2.x,q1.x,q2.x)&&intersect(p1.y,p2.y,q1.y,q2.y)&& dcmp(Cross(p2-p1,q1-p1)*Cross(p2-p1,q2-p1))!=1&& dcmp(Cross(q2-q1,p1-q1)*Cross(q2-q1,p2-q1))!=1; } bool isSS_s(Point p1,Point p2,Point q1,Point q2){ return dcmp(Cross(p2-p1,q1-p1)*Cross(p2-p1,q2-p1))==-1 &&dcmp(Cross(q2-q1,p1-q1)*Cross(q2-q1,p2-q1))==-1; } db Area(vector<Point> A){ // 多边形用 vector<point> 表示 , 逆时针 求面积 db ans=0; for (int i=0;i<A.size();i++) ans+=Cross(A[i],A[(i+1)%A.size()]); return ans/2; } //----------------PSLG------------------------- typedef vector<Point> Polygon; struct Edge{ int from,to; db ang; Edge(int f,int t,double a):from(f),to(t),ang(a){} }; const int maxn=1e6+10; struct PSLG{ int n,m,face_cnt;//face_cnt 面数 db x[maxn],y[maxn]; vector<Edge>edges;//储存边 vector<int>G[maxn];//指向边 int vis[maxn*2]; // 每条边是否已经访问过 int left[maxn*2]; // 左面的编号 int prev[maxn*2]; // 相同起点的上一条边(即顺时针旋转碰到的下一条边)的编号 vector<Polygon> faces;//faces 储存面 double area[maxn]; // 每个polygon的面积 void init(int n){ this->n = n; for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); faces.clear(); } //from->to的极角 double getAngle(int from, int to){ return atan2(y[to]-y[from], x[to]-x[from]); } void AddEdge(int from, int to){ edges.push_back((Edge){from, to, getAngle(from, to)}); edges.push_back((Edge){to, from, getAngle(to, from)}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } // 找出faces并计算面积 void Build(){ for(int u = 0; u < n; u++){ // 给从u出发的各条边按极角排序 int d = G[u].size(); for(int i = 0; i < d; i++) for(int j = i+1; j < d; j++) if(edges[G[u][i]].ang > edges[G[u][j]].ang) swap(G[u][i], G[u][j]); for(int i = 0; i < d; i++) prev[G[u][(i+1)%d]] = G[u][i]; } memset(vis, 0, sizeof(vis)); face_cnt = 0; for(int u = 0; u < n; u++) for(int i = 0; i < G[u].size(); i++){ int e = G[u][i]; if(!vis[e]){// 逆时针找圈 face_cnt++; Polygon poly; for(;;){ vis[e] = 1; left[e] = face_cnt; int from = edges[e].from; poly.push_back(Point(x[from], y[from])); e = prev[e^1]; if(e == G[u][i]) break; assert(vis[e] == 0); } faces.push_back(poly); } } for(int i = 0; i < faces.size(); i++){ area[i] = Area(faces[i]); } } }; //--------------------- PSLG pslg; int n,m,Q; Point p[1010][2],a[1010*1010]; vector<int>v[1010]; int cmp2(int x,int y){ return a[x]<a[y]; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); p[i][0]=Point(x1,y1);p[i][1]=Point(x2,y2); } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(isLL(p[i][0],p[i][1],p[j][0],p[j][1])){ a[m++]=getLL(p[i][0],p[i][1],p[j][0],p[j][1]); v[i].push_back(m-1);v[j].push_back(m-1); } } sort(v[i].begin(),v[i].end(),cmp2); } pslg.init(m); for(int i=0;i<m;i++){ pslg.x[i]=a[i].x;pslg.y[i]=a[i].y; } for(int i=1;i<=n;i++) if(v[i].size()>=2) for(int j=0;j<v[i].size()-1;j++) pslg.AddEdge(v[i][j],v[i][j+1]); pslg.Build(); sort(pslg.area,pslg.area+pslg.face_cnt); //for(int i=0;i<pslg.face_cnt;i++) // printf("%.6f\n",pslg.area[i]); printf("%d %.6f %.6f\n",pslg.face_cnt-1,pslg.area[pslg.face_cnt-1],pslg.area[1]); scanf("%d",&Q);int k; while(Q--){ scanf("%d",&k); if(k<pslg.face_cnt)printf("%.6f\n",pslg.area[pslg.face_cnt-k]); else printf("Invalid question\n"); } return 0; }