A

知识点:计算几何
难度预估:1900
题意:给两个点和一个圆,用一条曲线(或直线)连结两点但不能触碰到圆内部,求线的最短距离。
思路:首先要明确的是两种使用直线的情况:第一个是圆心到过两点的直线的距离大于或等于 。第二个是圆心在线段的两侧。
第一个判断很简单,求点到直线距离可以用面积除以2倍高的方法。
第二个判断只需要判断对应角是钝角就可以了,钝角可以用点乘为负数判断。
然后就是使用曲线的情况,曲线可以分为两个线段和一段圆弧,分别求长度即可。注意圆弧长为 ,其中 为对应圆心角。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pi 3.14159265358979
struct point{
    double x,y;
};
double dis(point a,point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int jud(point a,point b,point c){
    return (b.x-a.x)*(b.x-c.x)+(b.y-a.y)*(b.y-c.y)<=0;
}
double ar(point a,point b,point c){
    double pa=dis(a,b),pb=dis(a,c),pc=dis(b,c);
    double p=(pa+pb+pc)/2;
    return sqrt(p*(p-pa)*(p-pb)*(p-pc));
}
double dis(point a,point b,point c){
    return 2*ar(a,b,c)/dis(b,c);
}
double ang(point a,point b,point c){
    double A=dis(b,c),B=dis(a,c),C=dis(a,b);
    return acos((A*A+C*C-B*B)/(2*A*C));
}
int main(){
    point p1,p2,p3;
    double r;
    cin>>p1.x>>p1.y>>p2.x>>p2.y>>p3.x>>p3.y>>r;
    if(jud(p3,p1,p2)||jud(p3,p2,p1)){
        printf("%.7lf",dis(p1,p2));
        return 0;
    }
    if(dis(p3,p1,p2)>=r){
        printf("%.7lf",dis(p1,p2));
        return 0;
    }
    double d1=sqrt(dis(p1,p3)*dis(p1,p3)-r*r),d2=sqrt(dis(p2,p3)*dis(p2,p3)-r*r);
    double rd=r*(ang(p1,p3,p2)-acos(r/dis(p1,p3))-acos(r/dis(p2,p3)));
    printf("%.7lf",d1+d2+rd);
}

B

知识点:排序,尺取/二分
题意:选出尽可能多的数,使得 不大于
预估难度:1300
思路:应该是见过很多类似的原题了。排序后遍历左端点,尺取或二分右端点即可。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
    long long n,m,i,a[555555];
    ll t;
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(i=0;i<n;i++)cin>>a[i]; 
        sort(a,a+n);
        for(i=0;i<n;i++)if(a[i]-a[n]>m)break;
        ll ma=i,r=i;
        for(i=1;i<n;i++){
            while(r<n&&a[r]<=a[i]+m)r++;
            ma=max(ma,r-i);
        }
        cout<<ma<<endl;
    }
}

C

知识点:dfs
预估难度:1800
题意:类似围棋的规则,将一个连通块围起来,使其没有外气。
思路:这道题稍微要一点思维或者小技巧。我的方法是先把连通块的外部所有点标记,然后对应每个点check已标记的邻点即可。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
char a[555][555];
ll n,m;
void dfs(int x,int y){
    a[x][y]='x';
    if(x>0&&a[x-1][y]=='.')dfs(x-1,y);
    if(y>0&&a[x][y-1]=='.')dfs(x,y-1);
    if(x<n-1&&a[x+1][y]=='.')dfs(x+1,y);
    if(y<m-1&&a[x][y+1]=='.')dfs(x,y+1);
}
int main(){
    int i,j;
    cin>>n>>m;
    for(i=0;i<n;i++)cin>>a[i];
    dfs(0,0);
    for(i=0;i<n;i++){
        for(j=0;j<m;j++){
            if(a[i][j]=='#'){
                if(a[i-1][j]=='x')a[i-1][j]='*';
                if(a[i+1][j]=='x')a[i+1][j]='*';
                if(a[i][j-1]=='x')a[i][j-1]='*';
                if(a[i][j+1]=='x')a[i][j+1]='*';
            }
        }
    }
    for(i=0;i<n;i++){
        for(j=0;j<m;j++)
            if(a[i][j]=='x')a[i][j]='.';
    }
    for(i=0;i<n;i++)cout<<a[i]<<endl;
}

D

知识点:差分、前缀和
预估难度:1700
题意:每次对应矩形内部所有点权值加1。然后多次询问某矩形内部权值之和。
思路:二维差分前缀和板子题。(x1,y1),(x2,y2)对应矩形权值加一,只需要(x1,y1)、(x2+1,y2+1)两点加一,(x1,y2+1)、(x2+1,y1)两点减一之后做二维前缀和即可。询问时同理,需要预处理前缀和、之后每次询问对应4点即可。
(吐槽:卡cin超时惨惨惨)
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[2020][2020];
const int mod = 1e9+7;
int main(){
    int n,m,k,q,i,j;
    cin>>n>>m>>k>>q;
    while(k--){
        ll x1,y1,x2,y2;
        scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
        a[x1][y1]++;
        a[x2+1][y2+1]++;
        a[x1][y2+1]--;
        a[x2+1][y1]--;
    }

    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            a[i][j]+=a[i][j-1];
        }
    }
    for(j=1;j<=m;j++){
        for(i=1;i<=n;i++)
            a[i][j]+=a[i-1][j];
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            a[i][j]+=a[i][j-1];
        }
    }
    for(j=1;j<=m;j++){
        for(i=1;i<=n;i++)
            a[i][j]+=a[i-1][j];
    }
    while(q--){
        ll x1,y1,x2,y2;
        scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
        printf("%lld\n",a[x2][y2]+a[x1-1][y1-1]-a[x2][y1-1]-a[x1-1][y2]);
    }

}

E

知识点:最短路、dfs
预估难度:2000
题意:给一个无向图,把所有最短路的并集删了,问所有点是否仍然连通。
思路:先用dijkstra求最短路并用dfs标记,然后再dfs check一下连通块即可。
(吐槽:没开ll导致wa了惨惨惨)
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll MAXN = 1e5 + 5;
const ll MAXM = 1e6 + 5;
long long head[MAXN],d[MAXN],nxt[MAXM],to[MAXM],val[MAXM],vis[MAXM],sze=1,n,m;
inline void AddEdge(int u, int v, int w) {
    nxt[++sze]=head[u];
    to[head[u]=sze]=v;
    val[sze]=w;
}
struct HeapNode{
    ll dist, u;
    HeapNode(ll dist=0,ll u=0) : dist(dist),u(u){}
    bool operator <(const HeapNode& rhs)const{
        return dist>rhs.dist;
    }
};
void dij(int s){
    int i;
    for(i=1;i<=n;i++)d[i]=1e17;
    d[s]=0;
    priority_queue<HeapNode> q;
    q.push(HeapNode(0,s));
    while (!q.empty()) {
        while(!q.empty()&&q.top().dist!=d[q.top().u])q.pop();
        if(q.empty())break;
        HeapNode t=q.top();
        q.pop();
        for(ll e=head[t.u];e;e=nxt[e]){
            if(d[to[e]]>d[t.u]+val[e]){
                d[to[e]]=d[t.u]+val[e];
                q.push(HeapNode(d[to[e]],to[e]));
            }
        }
    }
}
long long ans=0;
void dfs(int u){
    if(u==1) return;
    for(int e=head[u];e;e= nxt[e]) {
        if(!vis[e]&&d[to[e]]+val[e]==d[u]){
            ans+=val[e],vis[e]=vis[e^1]=1;
            dfs(to[e]);
        }
    }
}
long long jud[MAXN],cnt;
void check(int u){
    jud[u] = 1;
    cnt++;
    for(int e=head[u];e;e=nxt[e]){
        if(!jud[to[e]]&&!vis[e])check(to[e]);
    }
}
int main(){
    int i;
    scanf("%lld%lld",&n,&m);
    for(i=1;i<=m;i++) {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        AddEdge(u,v,w);
        AddEdge(v,u,w);
    }
    dij(1);
    dfs(n);
    check(1);
    cnt==n?puts("YES"):puts("NO");
    return 0;
}

F

知识点:无
预估难度:600
题意:给定大小关系,判断两个字符串的大小。
思路:模拟即可。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
    string a,b;
    cin>>a>>b;
    if((a[0]=='t'&&b[0]=='e')||(a[0]=='c'&&b[0]=='t')||(a[0]=='m'&&b[0]=='c')||(a[0]=='e'&&b[0]=='m'))
        cout<<"tiangou txdy";
    else cout<<"tiangou yiwusuoyou";
}

G

知识点:贪心
预估难度:1000
题意:给定一些数以及一个上限k,选出尽可能多的数,满足这些数之和不超过k。
思路:显然从小到大取为优。排序后模拟即可。(也可以用最小堆不过没必要)
代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    long long n,m,i,a[555555];
    cin>>n>>m;
    for(i=0;i<n;i++)cin>>a[i];
    sort(a,a+n);
    long long sum=0;
    for(i=0;i<n;i++){
        if(sum+a[i]>m)break;
        sum+=a[i];
    }
    cout<<i;
}

H

知识点:并查集、离散化
预估难度:1700
题意:给定一些人之间的关系(朋友或敌人),已知朋友的朋友也是朋友。问是否矛盾。
思路:离线处理。先把所有朋友的关系用并查集连结,然后查询是否存在某对敌人是朋友。
注意要先离散化。本题数据量很大所以要使用sort离散化而不能用map
(我就是因为用map,wa了十几次,惨惨惨)
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
int a[1111111][3];
const int mod = 1e9+7;
ll fa[2222222],kdm[2222222];
int f(int x){
    if(fa[x]==x)return fa[x];
    return fa[x]=f(fa[x]);
}
void uni(int x,int y){
    int p=f(x),q=f(y);
    if(p!=q){
        if(kdm[p]>kdm[q]){
            fa[q]=p;
            kdm[p]+=kdm[q]+1;
        }
        else{
            fa[p]=q;
            kdm[q]+=kdm[p]+1;
        }
    }
}
ll sub[2000020],a1[2000020];
int main(){
    int n,k,q,i,j,t;
    cin>>t;
    while(t--){
        map<int,int>m;
        cin>>n;
        for(i=1;i<=n;i++){
            for(j=0;j<3;j++)a[i][j]=read();
            a1[i]=sub[i]=a[i][0];
            a1[n+i]=sub[n+i]=a[i][1];

        }
        sort(sub+1,sub+2*n+1);
        int sz=unique(sub+1,sub+2*n+1)-sub;
        for(i=1;i <= 2*n; i++)
            a1[i]=lower_bound(sub,sub+sz,a1[i])-sub;
        for(i=1;i<=2*n;i++)fa[i]=i,kdm[i]=0;
        for(i=1;i<=n;i++){
            if(a[i][2]){
                uni(a1[i],a1[n+i]);
            }
        }
        for(i=1;i<=n;i++){
            if(!a[i][2]&&f(a1[i])==f(a1[n+i]))break;
        }
        if(i==n+1)cout<<"YES\n";
        else cout<<"NO\n";
    }
}
/*
2
3
1 2 1
2 3 1
1 3 0
*/

I

知识点:dfs序、线段树
预估难度:1900
题意:给一棵树,有多次操作,分别为单点修改、询问子树权值和。
思路:先用dfs序,将树形结构转化为线性结构,然后就变成查询区间和,可以用线段树解决。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}
const int MAXN = 2000005;

int to[MAXN<<1],Next[MAXN<<1],head[MAXN],tol;

void add_edge(int u,int v){
    Next[++tol]=head[u];to[tol]=v;head[u]=tol;
    Next[++tol]=head[v];to[tol]=u;head[v]=tol;
}

int L[MAXN], R[MAXN], id[MAXN], dfn;

ll sum[MAXN<<2];

int n,m,k;

int val[MAXN];
void dfs(int u, int f){
    L[u]=++dfn;
    id[dfn]=u;
    for(int e=head[u];e;e=Next[e]){
        int v=to[e];
        if(v==f)continue;
        dfs(v,u);
    }
    R[u]=dfn;
}
void build(int nd,int l,int r){
    if(l==r){
        sum[nd]=val[id[l]];
        return;
    }
    ll mid=l+r>>1;
    build(nd<<1,l,mid);
    build(nd+nd+1,mid+1,r);
    sum[nd]=sum[nd<<1]+sum[nd+nd+1];
}

void update(int nd, int l, int r, int pos, int v){
    if(l==r){
        sum[nd]+=v;
        return;
    }
    ll mid=l+r>>1;
    if(pos<=mid)update(nd<<1,l,mid,pos,v);
    else update(nd+nd+1,mid+1,r,pos,v);
    sum[nd]=sum[nd<<1]+sum[nd+nd+1];
}

ll query(int nd, int l, int r, int L, int R){
    if(L<=l&&r<=R){
        return sum[nd];
    }
    ll mid=l+r>>1;
    ll res=0;
    if(L<=mid)res+=query(nd<<1,l,mid,L,R);
    if(R>=mid+1)res+=query(nd+nd+1,mid+1,r,L,R);
    return res;
}


int main(){
    n=read(),m=read(),k=read();
    for(int i=1;i<=n;++i)val[i]=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        add_edge(u,v);
    }
    dfs(k,0);

    build(1,1,n);

    for(int i=1;i<=m;++i){
        int op;
        op=read();
        if(op==1){
            int a=read(),x=read();
            update(1,1,n,L[a],x);
        }else{
            int a=read();
            ll ret=query(1,1,n,L[a],R[a]);
            cout<<ret<<endl;
        }
    }

    return 0;
}

J

知识点:前缀和、dp
预估难度:1800
题意:给定 个数,求
思路:我们可以假定前 个数的上述算式的结果为 ,那么对于第 个数而言,相较于前 个数,新增的部分为:



后两部分可以用前缀和预处理达成 ,因此总复杂度可以 解决。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[555555],sum[555555],sum2[555555],dp[555555];
const int mod = 1e9+7;
int main(){
    int n,i,j;
    cin>>n;
    for(i=1;i<=n;i++)scanf("%lld",&a[i]);
    ll s=0,s2=0;
    for(i=1;i<=n;i++){
        s+=a[i];
        s2+=a[i]*a[i]%mod;
        s%=mod;
        s2%=mod;
        sum[i]=s;
        sum2[i]=s2;
    }
    for(i=2;i<=n;i++){
        dp[i]=dp[i-1]+(i-1)*a[i]%mod*a[i]%mod+sum2[i-1]-2*a[i]*sum[i-1]%mod;
        dp[i]=(dp[i]+mod)%mod;
    }
    cout<<dp[n];
}