2019牛客国庆集训派对day7题解

还没补完,慢慢补,能写多少是多少···

2016

题目描述

给出正整数 n 和 m,统计满足以下条件的正整数对 (a, b) 的数量:

  1. 1 ≤ \leq a ≤ \leq n, 1 ≤ \leq b ≤ \leq m
  2. a × \times × b 是 2016 的倍数。

思路

(a × \times × b)% mod = (a%mod × \times ×b%mod)%mod
所以如果找到一个a%mod == 0,那么 (n-a)/mod 就是%mod = 0的数量

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int n,m;
    while(cin>>n>>m){
        ll ans = 0;
        for(int i=1;i<=min(n,2016);++i){
            for(int j=1;j<=min(m,2016);++j){
                if(i*j%2016 == 0){
                    ans += 1LL*((n-i)/2016+1)*((m-j)/2016+1);
                    // 加一是因为加上本身
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

有向无环图

题目描述

Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始、点 v 结束的路径)。
为了方便,点用 1, 2, … \dots ,n 编号。
c o u n t ( x , y ) \mathrm{count}(x, y) count(x,y) 表示点 x 到点 y 不同的路径数量(规定 c o u n t ( x , x ) = 0 \mathrm{count}(x, x) = 0 count(x,x)=0),Bobo 想知道
∑ i = 1 n ∑ j = 1 n c o u n t ( i , j ) ⋅ a i ⋅ b j \sum_{i = 1}^n\sum_{j = 1}^n \mathrm{count}(i, j) \cdot a_i \cdot b_j i=1nj=1ncount(i,j)aibj 除以 ( 1 0 9 + 7 ) (10^9+7) (109+7)的余数。
其中, a i , b j a_i, b_j ai,bj是给定的数列。

思路

因为只需要知道到所有点的数量和,而不是到定点的数量和。所有利用 d p i dp_i dpi维护从 i i i点到 i i i能到达的所有点的 b b b数组之和,如果现在有点 x x x指向 i i i的边。那么状态转移的式子为:
d p x + = d p i dp_x += dp_i dpx+=dpi
d p x + = b i dp_x += b_i dpx+=bi
然后统计答案就OK了

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const int mod = 1e9+7;
vector<int> g[maxn];
int a[maxn],b[maxn];
int in[maxn],dp[maxn];
void init(int n){
    memset(in,0,sizeof(in));
    memset(dp,0,sizeof(dp));
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    for(int i=1;i<=n;++i)
        g[i].clear();
}
void Print(int n){

    cout<<"dp"<<endl;
    for(int i=1;i<=n;++i)
        cout<<i<<" "<<dp[i]<<endl;
    cout<<endl;
}
int main()
{
    //freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    int n,m,x,y;
    while(cin>>n>>m)
    {
        init(n);
        for(int i=1;i<=n;++i)
            cin>>a[i]>>b[i];
        for(int i=0;i<m;++i){
            cin>>x>>y;
            g[y].push_back(x);
            in[x]++;
        }
        queue<int> que;
        for(int i=1;i<=n;++i)
            if(in[i] == 0)
                que.push(i);

        while(!que.empty()){
            int now = que.front();
            que.pop();
            for(int i=0;i<g[now].size();++i){
                int x = g[now][i];
                if(x == now)
                    continue;
                dp[x] = (1LL*dp[x] + dp[now])%mod;
                dp[x] = (1LL*dp[x] + b[now])%mod;
                in[x]--;
                if(in[x] == 0)
                    que.push(x);

            }
        }
        ll ans = 0;
        for(int i=1;i<=n;++i)
            ans = (ans + (1LL*a[i]*dp[i])%mod)%mod;
        cout<<ans<<endl;
        //Print(n);
    }
    return 0;
}

Parenthesis

题目描述

规定一类字符串是平衡的,这类字符串的规定如下:

  1. 空串S认为是平衡的
  2. 如果 S 1 S_1 S1是平衡的,那么形如 S = ( S 1 ) S=(S_1) S=(S1),S也是平衡的
  3. 如果 A , B A,B A,B是平衡的,那么形如 S = A B S=AB S=AB,S也是平衡的

现在给定一个平衡的字符串,有 q q q个操作,每一个操作有两个数字 a i , b i a_i,b_i ai,bi
将原字符串的第 a i a_i ai b i b_i bi交换,问交换后的字符串是否依然平衡
数据规模为 1 e 5 1e5 1e5

思路

首先根据题目容易推出:

  1. 如果交换的两个字符相同,不影响字符串的平衡性
  2. 如果交换的两个字符 ′ ) ′ ')' )在前, ′ ( ′ '(' (在后,也不影响字符串的平衡性

现在需要考虑的就是最后一种情况:如果需要交换的两个字符 ′ ( ′ '(' (在前, ′ ) ′ ')' )在后
很容易想到一个思路:
统计字符串的前缀和,即有一个左括号和加一,一个右括号减一。
在上述说的情况下交换对于前缀和数组的影响就是对于交换坐标的左闭右开区间进行区间减法,出现非法的情况就是前缀和中出现负数。
这个时候已经可以直接套一个线段树的板子,但区间减法的线段树写起来有点麻烦,所以还可以再优化。
即直接套一个区间查询的板子,看区间内有没有小于2的数。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
const int INF = 0x3f3f3f3f;
char str[maxn];
int a[maxn],b[maxn],pre[maxn];
int tree[maxn<<2];
void build(int l,int r,int k){
    if(l > r)   return ;
    if(l == r){
        tree[k] = pre[l];
        return ;
    }
    int mid = (l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    tree[k] = min(tree[k<<1],tree[k<<1|1]);
}
int query(int l,int r,int ql,int qr,int k){
    if(l>qr || r<ql || l>r)
        return INF;
    if(l>=ql && r<=qr)
        return tree[k];
    int mid = (l+r)>>1;
    return min(query(l,mid,ql,qr,k<<1),query(mid+1,r,ql,qr,k<<1|1));
}
int main()
{
    //freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    int n,q;
    while(cin>>n>>q)
    {
        memset(pre,0,sizeof(pre));
        memset(tree,0,sizeof(tree));
        cin>>str+1;
        for(int i=0;i<q;++i)
            cin>>a[i]>>b[i];
        for(int i=1;i<=n;++i){
            if(str[i] == '(')
                pre[i] = pre[i-1]+1;
            else
                pre[i] = pre[i-1]-1;
        }
        build(1,n,1);
        for(int i=0;i<q;++i){
            if(str[a[i]] == str[b[i]])
                cout<<"Yes"<<endl;
            else{
                if(a[i] > b[i]) swap(a[i],b[i]);
                if(str[a[i]] == ')' && str[b[i]] == '(')
                    cout<<"Yes"<<endl;
                else{
                    int now = query(1,n,a[i],b[i]-1,1);
                    if(now < 2) cout<<"No"<<endl;
                    else    cout<<"Yes"<<endl;
                }
            }
        }
    }
    return 0;
}

三角形和矩形

题目描述

obo 有一个三角形和一个矩形,他想求他们交的面积。
具体地,三角形和矩形由 8 个整数 x 1 , y 1 , x 2 , y 2 , x 3 , y 3 , x 4 , y 4 x_1, y_1, x_2, y_2, x_3, y_3, x_4, y_4 x1,y1,x2,y2,x3,y3,x4,y4描述。
表示三角形的顶点坐标是 ( x 1 , y 1 ) , ( x 1 , y 2 ) , ( x 2 , y 1 ) (x_1, y_1), (x_1, y_2), (x_2, y_1) (x1,y1),(x1,y2),(x2,y1)

矩形的顶点坐标是 ( x 3 , y 3 ) , ( x 3 , y 4 ) , ( x 4 , y 4 ) , ( x 4 , y 3 ) (x_3, y_3), (x_3, y_4), (x_4, y_4), (x_4, y_3) (x3,y3),(x3,y4),(x4,y4),(x4,y3)

思路

计算几何板子题,需要注意不能用cout,不然会WA···

代码

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-10;
const double PI=acos(-1.0);

struct Point{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;

Vector operator -(Point a,Point b){
    return Vector(a.x-b.x,a.y-b.y);
}
Vector operator +(Point a,Point b){
    return Vector(a.x+b.x,a.y+b.y);
}
Vector operator *(Vector a,double p){
    return Vector(a.x*p,a.y*p);
}
Vector operator /(Vector a,double 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);//在有精度需求,比如使用lower_bound的时候,加上dcmp()
}
int dcmp(double x){
    if(fabs(x)<eps)return 0;
    if(x<0)return -1;
    return 1;
}
bool operator ==(const Point& a,const Point& b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
double Cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}//叉积
Point GetlineIntersection(Point p,Vector v,Point q,Vector w){
    Vector u=p-q;
    double t=Cross(w,u)/Cross(v,w);
    return p+v*t;
}
typedef vector<Point> Polygon;
//多边形的有向面积,逆时针为正
double PolygonArea(Point po[],int n) {
    double area = 0.0;
    for(int i = 1; i < n-1; i++) {
        area += Cross(po[i]-po[0], po[i+1]-po[0]);
    }
    return area * 0.5;
}
double PolygonArea(Polygon po) {
    int n=po.size();
    double area = 0.0;
    for(int i = 1; i < n-1; i++) {
        area += Cross(po[i]-po[0], po[i+1]-po[0]);
    }
    return area * 0.5;
}
double cross(Point a,Point b,Point c)
{
    return Cross(a-c,b-c);
}
double CPIA(Point a[], Point b[], int na, int nb){//传入两个三角形,求相交部分的凸包
    Point p[20], tmp[20];           //复制点集与临时点集(P其实可以用B来做
    int tn, sflag, eflag;           //每轮相交凸包的点,叉乘符号
    a[na] = a[0], b[nb] = b[0];     //末点用初点复制方便首末点连边
    memcpy(p,b,sizeof(Point)*(nb + 1));         //把B复制到P
    for(int i=0;i<na&&nb>2;i++){                //扫一次A
        sflag=dcmp(cross(a[i+1],p[0],a[i]));    //取A两点与B第一点求叉乘符号
        for(int j=tn=0;j<nb;j++,sflag=eflag){   //扫一次B,更新TMP,TN是点数
            if(sflag>=0)tmp[tn++]=p[j];         //叉乘为正就是B数组的那个点压入
            eflag=dcmp(cross(a[i+1],p[j+1],a[i]));//求叉乘符号
            if((sflag^eflag)==-2)tmp[tn++]=GetlineIntersection(a[i],a[i+1]-a[i],p[j],p[j+1]-p[j]);
            //tmp[tn++]=intersection(a[i],a[i+1],p[j],p[j+1]);//求交点
        }
        memcpy(p, tmp, sizeof(Point) * tn);     //把TMP复制到P
        nb = tn, p[nb] = p[0];//TN即TMP点数记到NB
    }//其实该是NP表示P数组个数,这里省了个变量就用NB表示,下面第二行做参数而已
    if(nb < 3) return 0.0;      //相交部分凸包不够三个点,面积就是0
    return PolygonArea(p, nb);  //求出相交凸包部分的面积
}
double SPIA(Point a[], Point b[], int na, int nb){//传入两个多边形的点
    int i,j;                            //循环变量
    Point t1[4],t2[4];                  //其实T13与T23没用上
    double res=0,num1,num2;             //答案初始化,及叉乘符号
    a[na]=t1[0]=a[0],b[nb]=t2[0]=b[0];  //初始化T1,T2和ANA,BNB
    for(i=2;i<na;i++){                  //扫一次第一个多边形全部点
        t1[1]=a[i-1],t1[2]=a[i];        //每次在第一个多边形取两个点赋给T11,T12
        num1=dcmp(cross(t1[1],t1[2],t1[0]));//求出叉乘符号
        if(num1<0)swap(t1[1],t1[2]);    //小于0则改变T11,T12可使叉乘符号变正,实即改变T1三个点的顺逆
        for(j=2;j<nb;j++){              //扫一次第二个多边形全部点
            t2[1]=b[j-1],t2[2]=b[j];    //然后再在第二个多边形取两个点赋给T21,T22
            num2=dcmp(cross(t2[1],t2[2],t2[0]));//求出叉乘符号
            if(num2<0)swap(t2[1],t2[2]);//小于0则改变T11,T12可使叉乘符号变正,实即改变T1三个点的顺逆
            res+=CPIA(t1,t2,3,3)*num1*num2;       //累加相交部分面积
        }
    }
    res=fabs(res);
    return res;
}
Point a[5],b[5];
int main()
{
    //freopen("in.txt","r",stdin);
    int x1,x2,x3,x4,y1,y2,y3,y4;
    while(scanf("%d %d %d %d",&x1,&y1,&x2,&y2)!=EOF){
        scanf("%d %d %d %d",&x3,&y3,&x4,&y4);
        a[0].x = x1;a[0].y = y1;
        a[1].x = x1;a[1].y = y2;
        a[2].x = x2;a[2].y = y1;

        b[0].x = x3;b[0].y = y3;
        b[1].x = x3;b[1].y = y4;
        b[2].x = x4;b[2].y = y4;
        b[3].x = x4;b[3].y = y3;
        double s1 = SPIA(a,b,3,4);
        printf("%.8f\n",fabs(s1));
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值