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 ×\timesb%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 編號。
count(x,y)\mathrm{count}(x, y) 表示點 x 到點 y 不同的路徑數量(規定 count(x,x)=0\mathrm{count}(x, x) = 0),Bobo 想知道
i=1nj=1ncount(i,j)aibj\sum_{i = 1}^n\sum_{j = 1}^n \mathrm{count}(i, j) \cdot a_i \cdot b_j 除以 (109+7)(10^9+7)的餘數。
其中,ai,bja_i, b_j是給定的數列。

思路

因爲只需要知道到所有點的數量和,而不是到定點的數量和。所有利用dpidp_i維護從ii點到ii能到達的所有點的bb數組之和,如果現在有點xx指向ii的邊。那麼狀態轉移的式子爲:
dpx+=dpi dp_x += dp_i
dpx+=bi dp_x += b_i
然後統計答案就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. 如果S1S_1是平衡的,那麼形如S=(S1)S=(S_1),S也是平衡的
  3. 如果A,BA,B是平衡的,那麼形如S=ABS=AB,S也是平衡的

現在給定一個平衡的字符串,有qq個操作,每一個操作有兩個數字ai,bia_i,b_i
將原字符串的第aia_ibib_i交換,問交換後的字符串是否依然平衡
數據規模爲1e51e5

思路

首先根據題目容易推出:

  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 個整數 x1,y1,x2,y2,x3,y3,x4,y4x_1, y_1, x_2, y_2, x_3, y_3, x_4, y_4描述。
表示三角形的頂點座標是 (x1,y1),(x1,y2),(x2,y1)(x_1, y_1), (x_1, y_2), (x_2, y_1)

矩形的頂點座標是 (x3,y3),(x3,y4),(x4,y4),(x4,y3)(x_3, y_3), (x_3, y_4), (x_4, y_4), (x_4, y_3)

思路

計算幾何板子題,需要注意不能用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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章