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;
}
View Code
#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
*/
View Code

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
*/
View Code

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 
*/
View Code

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
 
 
*/
View Code

 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;
        
}
View Code

 

posted @ 2019-07-21 12:32  一入OI深似海  阅读(261)  评论(0编辑  收藏  举报