2.25-3.2 周记

2.25-3.2

1. 计算几何

1.1 二维几何基础

struct Point{
    double x,y;
    Point(double x = 0, double y = 0):x(x),y(y){}
}
typedef Point Vector;
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 ,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); }
bool operator < (const Point &a,const Point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double eps = 1e-10;
int dcmp(double 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;
}
//atan2计算极角

1.1.1 基本运算

double Dot(Vector A,Vector B){ return A.x*B.x+A.y*B.y; }
double Length(Vector A){return sqrt(Dot(A,A)); }
double Angle(Vector A,Vector B){ return acos(Dot(A,B)/Length(A)/Length(B)); }
double Cross(Vector A,Vector B){ return A.x*B.y - A.y*B.x;}
double Area2(Point A,Point B,Point C){ return Cross(B-A,C-A); }

Vector Rotate(Vector A,double rad){
    return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
}
Vector Normal(Vector A){
    double L = Length(A);
    return Vector(-A.y/L,A.x/L);
}

1.1.2 点与直线

//直线用P = P0+tv表示
//调用前确保两条直线P+tv,Q+tw有唯一交点,当且仅当Cross(v,w)非0
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;
}

double DistanceToLine(Point P,Point A,Point B){
    Vector v1 = B-A,v2 = P-A;
    return fabs(Cross(v1,v2))/Length(v1);//不取绝对值得到有向距离
}

double DistanceToSegment(Point P,Point A,Point B){
    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){
    Vector v = B-A;
    return A+v*(Dot(v,P-A)/Dot(v,v));
}
//线段相交 严格相交
bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2){
    double c1 = Cross(a2-a1,b1-a1),c2 = Cross(a2-a1,b2-a1);
    double c3 = Cross(b2-b1,a1-b1),c4 = Cross(b2-b1,a2-b1);
    return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
}
//不严格相交,还没有补充
bool Onsegment(Point p,Point a1,Point a2){
    return dcmp(Cross(a1-p,a2-p))==0 && dcmp(Dot(a1-p,a2-p)) < 0;
}

1.1.3 多边形

//多边形
double ConvexPolygonArea(Point *p,int n){
    double area = 0;
    for(int i=1;i<n-1;i++)
        area += Cross(p[i]-p[0],p[i+1]-p[0]);
    return area/2;
}
double PolygonArea(Point *p,int n){
    return ConvexPolygonArea(P,n);
}

1.2 与圆有关的计算问题

2. CF-1130

D题和E题

  • 火车只能顺时针走
  • 一次只能从一个地方拿走一个,所以对于当前 j ,如果有cnt[j]个糖果,那么必须经过它cnt[j]次,而且把需要传递距离最近的那个留到最后。这样该点需要走的最远距离就是(cnt[j]-1)*n + last ,last为最后一个要走的距离。
  • (O(n^2)) 遍历所有。输出答案
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 5010;
int n,m,cnt[N],last[N];
int main(){
    cin>>n>>m;
    memset(last,0x3f,sizeof last);
    for(int i=0,a,b;i<m;i++){
        scanf("%d%d",&a,&b);
        b = (b+n-a)%n;
        cnt[a]++;
        last[a] = min(last[a],b);
    }
    for(int i=1;i<=n;i++){
        int res = 0;
        for(int j=1;j<=n;j++)if(cnt[j])
           res = max(res,(cnt[j]-1)*n + last[j] + (j-i+n)%n);
        printf("%d ",res);
    }
    puts("");
    return 0;
}

3. CF-1038

C - Gambling

  • 这题很简单,贪心就可以了,奈何我脑痴把数组下标写的小了,浪费了二十分钟

D - Slime

  • 每个数给答案的贡献无非只有两种,一种正的,一种负的,让我们想想极端的情况。假如这个序列整个都是正的,那我们需要一个数来充当被减数,然后之后整个序列就可以进行运算了。当我们运算出当前结果时,可以想到的是,该结果可以进行内部取反,来迎合下一次操作。(妙啊)
#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
typedef long long ll;
ll a[N];
int n;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    if(n==1)cout<<a[1]<<endl;
    else{
        ll mi = 1e9,cnt_pos=0,cnt_neg=0,res=0;
        for(int i=1;i<=n;i++){
            mi = min(mi,abs(a[i]));
            res+=abs(a[i]);
            if(a[i]>0)cnt_pos++;
            else if(a[i]<0)cnt_neg++;
        }
        if(cnt_pos&&cnt_neg)
            cout<<res<<endl;
        else cout<<res-2*mi<<endl;
    }
    return 0;
}

4. CF-1025

B - Weakened Common Divisor

  • 这个题差点就想出来了,然而我太着急。
  • 每组两个数字,从中取出一个,然后整个序列很n个组,使得取出来的这n个数字的gcd不为1
  • 因为每组两个数字都可以取,所以可以提供的质因子就变成了两份,为了产生对答案的贡献,我们不妨把该组可以产生的贡献都计算进去。然后筛选出来的答案再进行压缩,使得他符合所有组中的选择。
  • 每组的数乘起来,然后求整个序列的gcd,可以想到这个gcd包含了所有组中共有的质因数(质因数的指数是多少不用管)。然后每次筛的时候,选一个质因数不为一的数字选就可以了。
#include <bits/stdc++.h>
using namespace std;
#define g std::__gcd
long long A[555555],B[555555],n,a=0;
int main(){
    scanf("%lld", &n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&A[i],&B[i]);
        a=g(a,A[i]*B[i]);
    }
    if(a==1){
        return puts("-1"),0;
    }
    for(int i=1;i<=n;i++){
        if(g(a,A[i])>1)
            a=g(a,A[i]);
        else
            a=g(a,B[i]);
    }
    return printf("%lld
", a), 0;
}

C - Plasticine zebra

  • 取一个切点然后反转,两次反转我们可以发现完全就是将前缀串接到了后缀串的后面,而一次反转效果也是同样的,把切割点分开,两端连上,中间的情况我们不需要考虑。
  • 所以这个题就转换为了把这个串结成一个圈,然后在圈上取一段长度最长不超过n的连续不同序列。注意极端情况(如果不是cf给的数据,恐怕要卡很久)
#include <bits/stdc++.h>
using namespace std;
char s[200100];
int main(){
    cin>>s;
    int res = 0, cnt = 1;
    int len = strlen(s);
    for(int i=len;i<len+len;i++)
        s[i] = s[i-len];
    for(int i=0;i<2*len-1;i++)
        if(s[i]==s[i+1]){
            res = max(res,cnt);
            cnt = 1;
        }
        else cnt++;
    res = max(res,cnt);
    if(res>len)
        cout<<len<<endl;
    else 
        cout<<res<<endl;
    return 0;
}

D - Recovering BST

  • 区间dp,思路挺顺,但是有一点马虎了之后就会造成很大的麻烦
  • dp[l][r][0]表示以 l-1 为根的区间可否组成二叉搜索树。然后搜就可以了
  • 代码可以优化一下
#include <bits/stdc++.h>
using namespace std;
const int N = 770;
int n;
int d[N][N][2],g[N][N],a[N];
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int dp(int l,int r,int m){
    int p = m==0?l-1:r+1;
    int &res = d[l][r][m];
    if(res)return res;
    if(l==r){
        if(g[l][p]>1)res = 1;
        else res = -1;
    }
    else{
        int flag = 0;
        for(int i=l;i<=r;i++){
            if(g[i][p]>1){
                if(i==l){
                    if(dp(i+1,r,0)==1){
                        flag = 1;break;
                    }
                }
                if(i==r){
                    if(dp(l,i-1,1)==1){
                        flag = 1;break;
                    }
                }
                else{
                    if(dp(l,i-1,1)==1&&dp(i+1,r,0)==1){
                        flag = 1;break;
                    }
                }
            }
        }
        if(flag)res = 1;
        else res = -1;
    }
    return res == 1;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        g[i][i] = a[i];
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            g[i][j] = g[j][i] = gcd(a[i],a[j]);
        }
    if(n == 2){
        if(g[1][2]>1)
        puts("Yes");
        else puts("No");
        return 0;
    }
    int flag = 0;
    for(int i=1;i<=n;i++){
        if(i==1){
            if(dp(2,n,0)==1){
                flag = 1;
                break;
            }
        }
        else if(i==n){
            if(dp(1,n-1,1)==1){
                flag = 1;break;
            }
        }
        else if(dp(1,i-1,1)==1&&dp(i+1,n,0)==1){
            flag = 1;break;
        }
    }
    /*
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++)
            printf("%d %d %d %d
",i,j,d[i][j][0],d[i][j][1]);
    }
    */
    if(flag)puts("Yes");
    else
    puts("No");
    return 0;
}

压缩后:

#include <bits/stdc++.h>
using namespace std;
const int N = 770;
int n;
int d[N][N][2],g[N][N],a[N];
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int dp(int l,int r,int m){
    int p = m==0?l:r;
    int &res = d[l][r][m];
    if(res)return res;
    if(l==r) res = 1;
    else{
        l = m==0?l+1:l;
        r = m==1?r-1:r;
        int flag = 0;
        for(int i=l;i<=r;i++){
            if(g[i][p]>1){
                if(dp(l,i,1)==1&&dp(i,r,0)==1){
                    flag = 1;break;
                }
            }
        }
        if(flag)res = 1;
        else res = -1;
    }
    return res == 1;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        g[i][i] = a[i];
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            g[i][j] = g[j][i] = gcd(a[i],a[j]);
        }
    int flag = 0;
    for(int i=1;i<=n;i++){
        if(dp(1,i,1)==1&&dp(i,n,0)==1){
            flag = 1;
            break;
        }
    }
    if(flag)puts("Yes");
    else puts("No");
    return 0;
}

5. CF-1027

B - Numbers on the Chessboard

  • 思路还是比较清晰的,不过在处理各种情况时,不要一味想着统一所有情况,其实分一下奇偶也是可以的,更容易写对
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,q,x,y;
int main(){
    cin>>n>>q;
    ll res;
    for(int i=1;i<=q;i++){
        cin>>x>>y;
        if((x+y)%2==0){
            if(x&1) res = ((x-1)/2)*n+(y/2+1);
            else res = ((x-2)/2)*n + ((n+1)/2)+y/2;
        }
        else {
            if(x&1) res = ((x-1)/2)*n + y/2 + (n*n+1)/2;
            else res = ((x-2)/2)*n + n/2 + y/2+1 + (n*n+1)/2;
        }
        cout<<res<<endl;
    }
    return 0;
}

C - Minimum Value Rectangle

  • 虽然是个难度1600的贪心,但是贪心策略很好想,难点在于处理上,如果用hash存储的话,即使是1e4的数组,每组test memset一下时间也会爆炸,貌似有人卡过的,不过这不是正解。
  • 正解是先排个序,然后两个相同的变成一个,单着的就抛弃了就可以了。然后扫一次
  • T了半小时之后看正解,因为数组下标,还有循环,又坑了半小时。感觉状态不太好的时候就不要硬干了,肯定是小问题。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000*1000+13;
int T,n,m,a[N],b[N];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        sort(a,a+n);
        int m = 0;
        for(int i=0;i<n-1;i++){
            if(a[i]==a[i+1]){
                b[m++] = a[i];
                i++;
            }
        }
        int A = b[0],B = b[1];
        ll P = (A+B)*ll(A+B);
        ll S = A*ll(B);
        for(int i=0;i<m-1;i++){
            ll TP = (b[i]+b[i+1])*ll(b[i]+b[i+1]);
            ll TS = b[i]*ll(b[i+1]);
            if(TS*P > S*TP){
                A = b[i];B = b[i+1];
                P = TP;
                S = TS;
            }
        }
        printf("%d %d %d %d
",A,A,B,B);
    }
    return 0;
}

D - Mouse Hunt

  • 这题一眼就是并查集,NONONO,忽略了两点的有向关系。也是巧了,刚做了这题第二天就在牛客碰到了。
  • 具体处理就是先拓扑扫一遍,剩下的就是环,在环内找个最小值放哨兵就可以了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200010;
int n;
int c[N],p[N],deg[N],v[N];

int dfs(int x){
    v[x] = 1;
    if(v[p[x]])return c[x];
    else return min(c[x],dfs(p[x]));
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&c[i]);
    for(int i=1;i<=n;i++){
        scanf("%d",&p[i]);
        deg[p[i]]++;
    }
    queue<int> q;
    for(int i=1;i<=n;i++)if(deg[i]==0)q.push(i);
    while(!q.empty()){
        int x = q.front();q.pop();
        deg[p[x]]--;
        if(deg[p[x]]==0)q.push(p[x]);
    }
    int res = 0;
    for(int i=1;i<=n;i++){
        if(deg[i]&&!v[i]){
            //printf("%d
",i);
            res+=dfs(i);
        }
    }
    cout<<res<<endl;
    return 0;
}

6.CF-1023

A - Single Wildcard Pattern Matching

  • 被A题卡特判了。难过
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
char s[N],t[N];
int n,m;
bool check(int x){
    if(x==n&&n!=m)return false;
    for(int i=0;i<x;i++)
        if(s[i] != t[i])return false;
    for(int i=n-1;i>x;i--){
        int j = m-1-(n-1-i);
        if(j<x)return false;
        if(s[i] != t[j])return false;
    }
    return true;
}
int main(){
    cin>>n>>m;
    cin>>s>>t;
    if(n>m+1){
        puts("NO");return 0;
    }
    int index = n;
    for(int i=0;i<n;i++)if(s[i]=='*'){
        index = i;break;
    }
    if(check(index))puts("YES");
    else puts("NO");
    return 0;
}

大佬的代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string s1,s2;int n,m,i=0;cin>>n>>m>>s1>>s2;
    while(s1[i]==s2[i]&&i<min(m,n))i++;
    while(s1[n-1]==s2[m-1]&&i<min(m,n)){n--;m--;}
    return cout<<(s1[i]=='*'&&n-i==1||s1==s2?"YES":"NO"),0;
}

C - Bracket Subsequence

  • 直接放代码了,这题没什么好说的,还卡了那么久。
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n,k;
char s[N],t[N];
int main(){
    while(cin>>n>>k){
        cin>>s;
        int l = 0,r = 0,p = 0;
        for(int i=0;i<n;i++){
            if(s[i] == '('){
                l++;
            }
            else{
                r++;
            }
            putchar(s[i]);
            if(l == k/2)break;
        }
        for(r = r+1;r<=l;r++)putchar(')');
        puts("");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/1625--H/p/10461355.html