2019牛客多校第二场 A,B,D,E,F,G,H,J

A-Eddy Walker

题目链接:https://ac.nowcoder.com/acm/contest/882/A

数论队友:https://blog.csdn.net/henucm/article/details/97111654

B-Eddy Walker2

题目链接:https://blog.csdn.net/henucm/article/details/99566371

数论队友:https://blog.csdn.net/henucm/article/details/99566371

D-Kth Minimum Clique

题目链接:https://ac.nowcoder.com/acm/contest/882/D

题目大意:给出一个n个顶点的无向加权图,求第k个最小加权群。两个不同的顶点相邻时,顶点的子集称之为团()

思路:bitset优化暴力求和。将每个点的相邻点用bitset表示,然后暴力求团。由于求的是第k小,所以我们将最小的求出来,依次加边,维护优先队列,直到第k小的团为止。注意一点,新加入的点必须能和之前的点都联通(子集)。同时为了保证不会重复加点,我们都从最后一个点开始加。

ACcode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
  
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define Pair pair<ll,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
  
const int MAXN=1e2+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
 
struct Node{
    ll SumW;
    bitset<MAXN> G;
    Node(ll _SumW=0,bitset<MAXN> _G=0){
        SumW=_SumW;G=_G;
    }
    friend int operator < (Node a,Node b){
        return a.SumW>b.SumW;
    }
};
bitset<MAXN> MP[MAXN];
int W[MAXN];
int n,k;
 
int main(){
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;++i) scanf("%d",&W[i]);
    for(int i=0;i<n;++i){
        char S[MAXN];scanf("%s",&S);
        for(int j=0;j<n;++j){
            MP[i][j]=S[j]-'0';
        }
    }
    bitset<MAXN> Tmp;Tmp.reset();
    priority_queue<Node> que;que.push(Node(0,Tmp));//从最小的团开始找
    while(que.size()){
        Node u=que.top();que.pop();--k;
//      printf("k=%d,u=(%lld,%d)\n",k,u.SumW,u.top);
        if(k==0){
            printf("%lld\n",u.SumW);
            return 0;
        }Tmp=u.G;
        int Pos=0;
        for(int i=0;i<n;++i){//从最后一个点开始枚举
            if(Tmp[i]) Pos=i+1;
        }
        for(int j=Pos;j<n;++j){
            if(Tmp[j]==0&&((Tmp&MP[j])==Tmp)){
                Tmp[j]=1;
                que.push(Node(u.SumW+W[j],Tmp));
                Tmp[j]=0;
            }
        }
    }printf("-1\n");
}

E-MAZE

题目链接:https://ac.nowcoder.com/acm/contest/882/E

文少DP+线段树:https://blog.csdn.net/henu_1710252529/article/details/102637240

F-Partition problem

题目链接:https://ac.nowcoder.com/acm/contest/882/F

题目大意:2n个人,分成红队或白队,每个队n人,使得竞争价值最大化。竞争价值是不同对中没对人的竞争价值之和。(任意两人之间的竞争价值之和)。给出一个邻接矩阵,表示的是i,j之间的竞争价值。

思路:枚举第一队的人,刷新竞争价值之和。二进制每一位表示该位置的人是否在1队。然后更新价值。一个二进制枚举题目,n并不大。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
  
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
  
const int MAXN=20+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

ll KP[MAXN][MAXN];
ll Ans;
int n;

void Solve(ll state,int cnt,int pre,ll Val){
	if(cnt==n/2){
		Ans=max(Ans,Val);
		return ;
	}
	if(n-pre-1+cnt<n/2) return ;
	for(int nxt=pre+1;nxt<n;++nxt){
		ll tempval=Val;
		for(int i=0;i<n;++i){
			if((state>>i)&1) tempval-=KP[nxt][i];
			else tempval+=KP[nxt][i];
		}Solve(state|(1ll<<nxt),cnt+1,nxt,tempval);
	}
}
int main(){
	scanf("%d",&n);n<<=1;
	for(int i=0;i<n;++i){
		for(int j=0;j<n;++j){
			scanf("%lld",&KP[i][j]);
		}
	}
	ll Val=0;
	for(int i=0;i<n;++i){
		Val+=KP[0][i];
	}Ans=0;
	Solve((1<<0),1,0,Val);
	printf("%lld\n",Ans);
}

G-Polygons

题目链接:https://ac.nowcoder.com/acm/contest/882/G

题目大意:给出n条直线,找出这n条直线,判断能够围成的多边形的数量,输出最大最小值,并输出前q个第qi大小的多边形面积。没有输出Invalid question

思路:我们用图的方法解决这道题。将所有的直线之间两两交点做成无向图。遍历每个点,每次找夹角最小的边,然后回归原点后,即可求出所求面积。

如图所示,红点表示的是线线之间的交点,绿线表示双向边。

枚举交点,找到最小回路。走完后删掉这条边(边权置为0)。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
       
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
       
const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e12;
const double PI=acos(-1.0);
const double EPS=1.0e-10;
//unsigned register
// ios::sync_with_stdio(false)
 
/*
最多有n^2个面积
求出交点
对于每条直线求出在这条直线上的交点
相邻的两条之间建立双向有向边
遍历每个点,取相邻的夹角最小的边
返回原点后,这条线即为所求面积。
*/
struct Point{
    int flag;
    double x,y;
    Point(double _x=0,double _y=0,int _flag=1){
        x=_x;y=_y;flag=_flag;
    }
    friend Point operator - (Point a,Point b){
        return Point(a.x-b.x,a.y-b.y);
    }
    friend double operator ^ (Point a,Point b){
        return a.x*b.y-a.y*b.x;
    }
};
struct V{
    Point str,end;
    V(Point _str=Point(0,0),Point _end=Point(0,0)){
        str=_str;end=_end;
    }
    void Input(){
        scanf("%lf%lf%lf%lf",&str.x,&str.y,&end.x,&end.y);
    }
    friend V operator - (V a,V b){
        return V(a.str-b.str,a.end-b.end);
    }
};
struct Node1{
    int v,val,nxt;
    Node1(int _v=0,int _val=1,int _nxt=0){
        v=_v;val=_val;nxt=_nxt;
    }
};
Node1 Edge[MAXN*MAXN<<2];
int Head[MAXN*MAXN],Ecnt;
V Vec[MAXN];
vector<Point> Dots;
vector<int> Line[MAXN];
int Stk[MAXN*MAXN<<2],top;
double Ans[MAXN*MAXN<<2];int tot;
int n,m;
 
void Intt(){
    clean(Head,-1);Ecnt=0;
}
void AddEdge(int u,int val,int v){
    Edge[Ecnt]=Node1(v,val,Head[u]);
    Head[u]=Ecnt++;
}
int Sgn(double x){
    if(fabs(x)<EPS) return 0;
    if(x<0) return -1;
    return 1;
}
int Parellel(V l1,V l2){
    return fabs((l1.end-l1.str)^(l2.end-l2.str))<EPS;
}
Point LineInterDot(V l1,V l2){
    if(Parellel(l1,l2)) return Point(0,0,0);
    Point p=l1.str;p.flag=1;
    double res=((l1.str-l2.str)^(l2.str-l2.end));
    res/=((l1.str-l1.end)^(l2.str-l2.end));
    p.x+=(l1.end.x-l1.str.x)*res;
    p.y+=(l1.end.y-l1.str.y)*res;
    return p;
}
int Cmp1(int a,int b){
    if(Sgn(Dots[a].x-Dots[b].x)==0) return Dots[a].y<Dots[b].y;
    return Dots[a].x<Dots[b].x;
}
int DFS(int u,int fa){
    Stk[++top]=u;
    if(Stk[top]==Stk[1]){//更新Ans
        double area=0;
        for(int i=1;i<top;++i) area+=((Dots[Stk[i]])^(Dots[Stk[i+1]]));
        Ans[++tot]=fabs(area)/2.0;
        return 1;
    }
    int can=-1;
    for(int i=Head[u];i+1;i=Edge[i].nxt){//遍历所有的相邻点,找到叉积最小的那个
        int v=Edge[i].v;
        if(v==fa||Edge[i].val==0) continue;
        if(Sgn((Dots[v]-Dots[u])^(Dots[u]-Dots[fa]))<0&&(can==-1||Sgn((Dots[v]-Dots[u])^(Dots[Edge[can].v]-Dots[u]))<0)){
            can=i;
        }
    }
    if(can==-1) return 0;
    if(DFS(Edge[can].v,u)){//返回原点
        Edge[can].val=0;
        return 1;
    }return 0;
}
int main(){
    scanf("%d",&n);Intt();
    for(int i=1;i<=n;++i) Vec[i].Input();
    for(int i=1;i<=n;++i){//第i条直线和其他直线的交点
        for(int j=i+1;j<=n;++j){
            Point v=LineInterDot(Vec[i],Vec[j]);
            if(v.flag==0) continue;
            //存在交点 i,j线段交点
            Dots.push_back(v);
            Line[i].push_back(Dots.size()-1);
            Line[j].push_back(Dots.size()-1);
        }
    }
    for(int i=1;i<=n;++i){//相邻的链接
        sort(Line[i].begin(),Line[i].end(),Cmp1);
        int len=Line[i].size();
        for(int j=0;j<len-1;++j){
            AddEdge(Line[i][j],1,Line[i][j+1]);
            AddEdge(Line[i][j+1],1,Line[i][j]);
        }
    }
    //得到一个从0开始的点集Dots,点集中的图
    //遍历每个点开始,找到最小回路
    int len=Dots.size();tot=0;
    for(int i=0;i<len;++i){
        for(int j=Head[i];j+1;j=Edge[j].nxt){
        //随机选取一个点作为起始边,向内围成一个多边形
        //删掉这个边
            int v=Edge[j].v;
            top=0;Stk[++top]=i;
            if(Edge[j].val&&DFS(v,i)){
                Edge[j].val=0;
            }
        }
    }sort(Ans+1,Ans+1+tot);
    printf("%d %.6lf %.6lf\n",tot,Ans[tot],Ans[1]);
    int m;scanf("%d",&m);
    while(m--){
        int x;scanf("%d",&x);
        if(x<=tot) printf("%.6lf\n",Ans[tot-x+1]);
        else printf("Invalid question\n");
    }
}

H-Second Large Rectangle

题目链接:https://ac.nowcoder.com/acm/contest/882/H

题目大意:给出一个n*m的01矩阵,找出第二小的全1矩阵。

思路:二维单调栈,枚举每一行,维护一个单增栈,每次记录大小。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
  
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
  
const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
 
struct Node{
    int l,r,h,S;
    Node(int _l=0,int _r=0,int _h=0){
		l=_l;r=_r;h=_h;
	}
    void IntS(){
        S=(r-l+1)*h;
    }
};
Node Area[MAXN][MAXN];
Pair Stk[MAXN];int top;
map<Pair,int> Vis;
char S[MAXN][MAXN];
int A[MAXN][MAXN];
int Sum[MAXN];
int n,m;


int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i) scanf("%s",&S[i]);
    for(int i=0;i<n;++i){
        for(int j=0;j<m;++j) A[i+1][j+1]=S[i][j]-'0';
    }
    for(int i=1;i<=n;++i){
//	   	printf("i=%d\n",i); 
        for(int j=1;j<=m;++j){
            if(A[i][j]) Sum[j]++;
            else Sum[j]=0;
        }
        top=0;
        Stk[++top]=make_pair(-1,0);
        for(int j=1;j<=m;++j){
			while(top>=1&&Stk[top].first>=Sum[j]) --top;
			Area[i][j].l=Stk[top].second+1;
			Area[i][j].h=Sum[j];
			Stk[++top]=make_pair(Sum[j],j);
		}
		top=0;
		Stk[++top]=make_pair(-1,m+1);
		for(int j=m;j>=1;--j){
			while(top>=1&&Stk[top].first>=Sum[j]) --top;
			Area[i][j].r=Stk[top].second-1;
			Stk[++top]=make_pair(Sum[j],j);
		}
        for(int j=1;j<=m;++j) Area[i][j].IntS();
//        for(int j=1;j<=m;++j) printf("j=%d l=%d r=%d h=%d\n",j,Area[i][j].l,Area[i][j].r,Area[i][j].h);
    }
    int fir=0,sec=0;
    for(int i=1;i<=n;++i){
    	Vis.clear();
        for(int j=1;j<=m;++j){
            Pair temp=make_pair(Area[i][j].l,Area[i][j].h);
            if(Vis[temp]) continue;
            Vis[temp]=1;
            if(Area[i][j].S>fir){
                sec=max(fir,max(Area[i][j].S-Area[i][j].h,Area[i][j].S-(Area[i][j].r-Area[i][j].l+1)));
                fir=Area[i][j].S;
                continue;
            }
            if(Area[i][j].S>sec) sec=Area[i][j].S;
//			printf("l=%d,r=%d,h=%d,S=%d\n",Area[i][j].l,Area[i][j].r,Area[i][j].h,Area[i][j].S);
//			printf("fir=%d,sec=%d\n",fir,sec);
        }
    }printf("%d\n",sec);
}

J-Subarray

题目链接:https://ac.nowcoder.com/acm/contest/882/J

题目大意:有一个1e9长度的全-1数组,现在里面有不超过1e7个1,求有多少个区间数量,区间内之和>0的。

思路:很迷,感觉好难,需要再研究研究。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
       
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
       
const int MAXN=1e6+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-10;
//unsigned register
// ios::sync_with_stdio(false)
 
/*
使用DP求出每段所在的连续段。
每次查询与上次查询的差距==1
从左到右枚举左端点,统计右边比当前值大的个数
*/
int L[MAXN],R[MAXN],Pre[MAXN],Nxt[MAXN];
int Sum[MAXN*30],Cnt[MAXN*30],Lazy[MAXN*30];
int n;
 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d%d",&L[i],&R[i]);
    }
    Nxt[1]=R[1]-L[1]+1;
    for(int i=2;i<=n;++i) Nxt[i]=max(0,Nxt[i-1]-(L[i]-R[i-1]-1))+R[i]-L[i]+1;
    Pre[n]=R[n]-L[n]+1;
    for(int i=n-1;i>=1;--i) Pre[i]=max(0,Pre[i+1]-(L[i+1]-R[i]-1))+R[i]-L[i]+1;
    ll Ans=0;
    for(int i=1,base=1e7,j;i<=n;i=j+1){
//      printf("i=%d\n",i);
        j=i+1;
        //处理出来足够覆盖的区间[i,j]
        while(j<=n&&Pre[j]+Nxt[j-1]>=L[j]-R[j-1]-1) ++j;--j;
        int left=max(0,L[i]-Pre[i]),right=min(1000000000-1,R[j]+Nxt[j]);
        int pos=i,mi=INF32,mx=0;
        Sum[0]=0;
        //处理出来前缀和,并且将之加和,确定前缀和区间
        for(int k=left;k<=right;++k){
//          printf("k=%d ",k);
            if(k>=L[pos]&&k<=R[pos]) Sum[k-left+1]=Sum[k-left]+1;
            else Sum[k-left+1]=Sum[k-left]-1;
            if(k==R[pos]) ++pos;
            mi=min(mi,Sum[k-left+1]+base);
            mx=max(mx,Sum[k-left+1]+base);
            Cnt[Sum[k-left+1]+base]++;
        }//printf("\n");
        //处理出来比前缀和大的数量
        for(int k=mx-1;k>=mi;--k) Cnt[k]+=Cnt[k+1];
        Ans+=Cnt[base+1];
        //遍历前缀和,求取每个位置比该位置大的数量
        //刷新Lazy标记
        for(int k=left;k<=right;++k){
//          printf("k=%d ",k);
            int tmp=Sum[k-left+1]+base;
            Cnt[tmp+1]-=Lazy[tmp+1];
            Lazy[tmp]+=Lazy[tmp+1]+1;
            Lazy[tmp+1]=0;
            Ans+=Cnt[tmp+1];
        }
        //初始化
        for(int k=mi;k<=mx;++k) Cnt[k]=Lazy[k]=0;
    }printf("%lld\n",Ans);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值