模板

模板:

矩阵快速幂:

//矩阵快速幂模板

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
using namespace std;
typedef pair<ll,ll> pii;
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
#define sf scanf
#define pf printf
const int mod=1e9+7;
const int MOD=10007;

inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<&#39;0&#39;||ch>&#39;9&#39;)&&ch!=&#39;-&#39;)ch=getchar();
    if(ch==&#39;-&#39;)t=true,ch=getchar();
    while(ch<=&#39;9&#39;&&ch>=&#39;0&#39;)x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}

/*priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆         小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下       大根堆      大到小
map<ll,ll>mp;*/

ll n,m,t,l,r,p;


struct mat{
    ll a[110][110];//矩阵的大小 
};
mat in,out;//in输入的矩阵,out输出的矩阵 

mat mull(mat x, mat y){
    mat c;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            c.a[i][j]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                c.a[i][j]=c.a[i][j]%mod+x.a[i][k]*y.a[k][j]%mod;

    return c;
} 

mat pow(mat x,ll y){
    mat ans;
    for(int i=1;i<=n;i++)//初始化成单位矩阵 就是对角线唯一,其余为零 
        ans.a[i][i]=1;
    while(y){
        if(y&1) ans=mull(ans,x);
        y>>=1;
        x=mull(x,x);        
    }
    return ans;    
}

int main(){
     sf("%lld%lld",&n,&m);
     for(int i=1;i<=n;i++)
         for(int j=1;j<=n;j++)
             sf("%lld",&in.a[i][j]);
     out=pow(in,m);
     for(int i=1;i<=n;i++)
         for(int j=1;j<=n;j++)
             if(j!=n)
            printf("%lld ",out.a[i][j]);
            else printf("%lld\n",out.a[i][j]);
    return 0;
}

最短路:
floyed:

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<ll,ll> pii;
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
const int maxn=2e5+1010;
#define inf 0x3f3f3f3f
#define sf scanf
#define pf printf
const int mod=998244353;
const int MOD=10007;

inline int read() {
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<&#39;0&#39;||ch>&#39;9&#39;)&&ch!=&#39;-&#39;)ch=getchar();
    if(ch==&#39;-&#39;)t=true,ch=getchar();
    while(ch<=&#39;9&#39;&&ch>=&#39;0&#39;)x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}

/*priority_queue<ll , vector<ll> , greater<ll> > mn;//上  小根堆         小到大 
priority_queue<ll , vector<ll> , less<ll> > mx;//下       大根堆      大到小
map<ll,ll>mp;*/

ll n,m,t,l,r,p;
ll sum,ans,res,cnt,flag,maxx,minn;
bool isprime[maxn];
ll a[maxn],b[maxn],c[maxn];
ll dis[maxn],vis[maxn];
ll dp[1010][1010];
string str,s;

#define read read()

//k是中间结点,i是起点,j是终点
void floyed(ll dp[][1010]){
    for(int k=0;k<n;k++){
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(dp[i][j]>dp[i][k]+dp[k][j]){
                    dp[i][j]=dp[i][k]+dp[k][j];
                }
            }
        }
    }
}
int main(){
    sf("%lld%lld",&n,&m);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            dp[i][j]=inf;
        }
        dp[i][i]=0;
        //如果需要判断又没没有回路把这个删掉。 
    }
    for(int i=0;i<m;i++){
        sf("%d%d%d",&x,&y,&z);
        dp[x][y]=dp[y][x]=z;
    }
    floyed(dp);

    //可以判断是否存在负环
    for(int i=0;i<n;i++){
        if(mp[i][i]<0){
            flag=true;
        }
    }
    if(flag) cout<<"存在负环"<<endl;
    else cout<<"不存在负环"<<endl;
// cout<<mp[0][n-1]<<endl;
    return 0;
}

二分图:
二分图定义:简单的理解就是两个点集,其中在内部没有连接的边。
其充要条件是:图 G 中至少存在两个点,且图中所有回路的长度均为偶数。
完全二分图:其中一个点集中与另一点集中的点一定有相对应的边连接,也就是没有孤立的点。
完全二分图

二分图中匹配的概念:
在边集中,任意两条边都没有公共顶点。
二分图中最大匹配的概念:
在匹配的条件下,使得边集中边最多。
二分图中的完全匹配概念:
简单来说,对于一个二分图,左点集中的每一个点都与右点集的一个点匹配,或者右点集中的每一个点都与左点集的一个点匹配。
二分图中的完美匹配概念:
对于一个二分图,左点集与右点集的点数相同,若存在一个匹配,包含左点集、右点集的所有顶点,则称为完美匹配。
简单来说,对于一个二分图,左点集中的每一个点都与右点集的一个点匹配,并且右点集中的每一个点都与左点集的一个点匹配。
二分图的判定:

int n;//节点数
vector<int> G[N];//G[i]表示i节点邻接的点
int color[N];//color[i]=0,1,2 表i节点不涂颜色、涂白色、涂黑***ool bipartite(int u)//判断无向图是否可二分
{
    for(int i=0;i<G[u].size();i++)//枚举结点
    {
        int v=G[u][i];//相邻节点
        if(color[v]==color[u])//若两结点颜色相同,无法二分
            return false;
        if(!color[v])//若结点没涂颜色
        {
            color[v]=3-color[u];//改变结点颜色,使v的颜色与u的颜色对立
            if(!bipartite(v))//若点v不满足二分图话,则无法二分
                return false;
        }
    }
    return true;
}

二分图-匈牙利算法:
交替路:
从一个未匹配点出发经过未匹配边->未匹配点->未匹配边....形成的路径。
未匹配点,边的概念是在二分图匹配中如果匹配的两个点叫匹配点,其边就是匹配边,未匹配的点就是未匹配点,其边就是未匹配边。
定义:设 M 为二分图 G 已匹配边的集合,若 P 是图 G 中一条连通两个未匹配点的路径(起点在 X/Y 部,终点在 Y/X 部),且属 M 的边(匹配边)与不属 M 的边(非匹配边)在 P 上交替出现,则称 P 为相对 M 的一条增广路径。

由于增广路的第一条边是没有参与匹配的,第二条边参与了匹配,...,最后一条边没有参与匹配,并且起点和终点还没有被选择过,显然 P 有奇数条边。
其中这样理解第一条边不参加匹配,第二条边参加匹配,因为你要建增广路,方式是交替路的方式,所以说如果第一条边认为是匹配边的话第一天边就不能连起来,就像下面的9->4如果任务他是匹配边,这个边就建不上,之后如果认为4->8为未匹配边的话,4这个点已经是匹配点了,不能再连边了,所以之间把他认为是匹配边,儿8还是未匹配点,这样才能继续之后的建图。

简单来说,从一个未匹配点出发,走交替路,若途径另一未匹配点(除起点外),则这条交替路称为增广路。

如下图,左图中的一条增广路如右图所示,图中的匹配点均用红色标出
主图 解释

增广路的性质:

1,增广路的长度一定是一定是奇数,起点跟终点一定是分别属于两个点集。均未匹配。保证不成环。
2,未匹配边比匹配边多1,因为是未->已->未->......->已->未。
3,P 经过取反操作可以得到一个更大的匹配 M’。
4,M 为 G 的最大匹配当且仅当不存在相对于 M 的增广路径。
3,4不是特别懂先放这吧

增广路定理:

由于增广路中间的匹配节点不存在其他相连的匹配边,因此交换增广路中的匹配边与非匹配边不会破坏匹配的性质。

由增广路性质可知,只要把增广路中的匹配边和非匹配边交换,交换后,图中的匹配边数目比原来多了 1 条。

故而,可以通过不停地找增广路来增加匹配中的匹配边和匹配点,找不到增广路时,即达到最大匹配,这就是增广路定理。
其实说简单点就是扩展路线。使得最长

匈牙利树

匈牙利树一般由 DFS 构造(类似于 DFS 树),其是从一个未匹配点出发进行 DFS(必须走交替路),直到不能再扩展为止。(达到最长路径)

如下图,通过左侧的二分图,进行 DFS 可以得到右侧的树,但这棵树存在一叶结点为非匹配点(7号),而匈牙利树要求所有叶结点均为匹配点,故这棵树不是匈牙利树。
图片说明 图片说明
但若原图中不含 7 号结点,那么从 2 号结点出发就会得到一棵匈牙利树,如下图
图片说明
图片说明
前导概念:
K-正则图:各顶点的度都是K的无向简单图。
最大匹配数:最大匹配的匹配边数
最大独立集数:选取最多的点集,使得点集中任意两点不相连。
最小点覆盖数:选取最少的点集,使得任意一条边至少有一个端点在点集中。
最大独立集数可以与最小点覆盖数比较理解
性质
最大匹配数=最小覆盖数
最大独立集数=顶点数-最大匹配数

最大匹配数/最小覆盖数

DFS:代码短容易理解 路线多时时间复杂度要高。

int n,m;
bool vis[N];//判断是否在交替路 
vector<int> G[N];//存边 
int link[N];//存连接点

bool dfs(ll x){
    for(int i=0;i<G[x].size();i++){
        int j=G[x][i];
        if(!vis[j]){
            vis[j]=1;
            if(link[j]==-1||dfs(link[j])){//如果是未匹配点,说明交替路是增广路
                link[j]=x;//交换路径
                return 1;                
            }
        }
    }
    return 0;    //不存在增广路,返回失败
} 

int print(){
    int ans=0;
    for(int i=1;i<=n;i++){//从左侧开始每个结点找一次增广路
        memset(vis,0,sizeof vis);
        if(dfs(i)) ans++;//找到一条增广路    
    }
    printf("%lld\n",ans);
}
int main (){
    while(~scanf("%lld%lld",&n,&m)){
        memset(link,-1,sizeof link);//全部初始化为未匹配点
        for(int i=0;i<N;i++)//每次清空 
            G[i].clear();
        while(m--){
            int l,r;
            scanf("%lld%lld",&l,&r);
            G[l].push_back(r);//建图 无向图 
            G[r].push_back(l);
        }
        print();//输出最大匹配数  
    }

    return 0;
}

BFS版本

int n,m;//左边点数,右边点数
int vis[N];//vis[i]表示是否在交替路中
int link[N];//存连接点
int pre[N];//存前驱结点
vector<int> G[N];//存边
queue<int> Q;
int hungarian(){
    memset(vis,-1,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    memset(link,-1,sizeof(link));

    int ans=0;//记录最大匹配数
    for(int i=1;i<=n;i++){
        if(link[i]==-1){//若点未匹配
            pre[i]=-1;//没有前驱
            while(!Q.empty())//清空队列
                Q.pop();

            Q.push(i);
            bool flag=false;
            while(!Q.empty() && !flag){
                int x=Q.front();
                for(int j=0;j<G[x].size();j++){//对x的每个邻接点
                    if(!flag)//如果falg为真,则说明找到一未匹配点,不必继续下去
                        break;

                    int y=G[x][j];
                    if(vis[y]!=i){//不在交替路中
                        vis[y]=i;//存入交替路
                        Q.push(link[y]);//交换路径
                        if(link[y]>=0)//在已匹配点中
                            pre[link[y]]=x;
                        else {//找到未匹配点,交替路变增广路
                            flag=true;
                            int d=x;
                            int e=y;
                            while(d!=-1){//找到一个未匹配点,无法构成匈牙利树,让所有点不断的往回更新,重选下一个
                                int temp=link[d];
                                link[d]=e;
                                link[e]=d;
                                d=pre[d];
                                e=temp;
                            }
                        }
                    }
                }
                Q.pop();
            }
            if(link[i]!=-1)//统计最大匹配数
                ans++;
        }
    }
    return ans;
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        printf("%d\n", hungarian());//输出最大匹配数
    }
    return 0;
}

最大独立集

int n,m;
bool vis[N];
int link[N];
bool G[N][N];
bool dfs(int x){
    for(int y=1;y<=m;y++){
        if(G[x][y]&&!vis[y]){
            vis[y]=true;
            if(link[y]==-1 || dfs(link[y])){
                link[y]=x;
                return true;
            }
        }
    }
    return false;
}
int hungarian(){
    int ans=0;
    for(int i=1;i<=n;i++){
        memset(vis,false,sizeof(vis));
        if(dfs(i))
            ans++;
    }
    return ans;
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF&&(n+m)){
        memset(link,-1,sizeof(link));
        memset(G,true,sizeof(G));

        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x][y]=false;//不满足条件则连一条边
        }

        int mate=hungarian();//最大匹配数
        int res=n-mate;//最大独立集

        printf("%d\n",res);
    }
    return 0;
}

与最短路中的dj建图思路有些相似。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1010,M = 1e5+10;
int n1,n2,m;
int h[N], e[M], ne[M], idx; //邻接表
int match[N];
bool st[N];

void add(int a,int b)//建图过程 
{
   e[idx] = b, ne[idx] = h[a], h[a] = idx++;     
} 

bool find(int x)
{
    for(int i = h[x]; i!=-1; i=ne[i]) // 遍历 x 男孩喜欢所有的女孩 
    {
          int j = e[i];
          if(!st[j]) // 如果st[j] = true 就不考虑这个女孩 
          {
             st[j] = true; // 标记 j 这个女孩,作用是如果这个女孩有男朋友,我们去找她男朋友有没有可能和别的女孩在一起时,就不需要考虑他现对象 j 了 
           if(match[j] == 0 || find(match[j]))// 女孩还没有对象或女孩的男朋友可以和其他喜欢的女生在一起 
           {
                match[j] = x;
                return true;
           }     
        }
    } 
    return false;
}

int main()
{
    int p;
    while(~scanf("%d",&p)){
        if(p==0) return 0;
        else cin>>n1>>n2;
        memset(h,-1,sizeof(h));
        memset(e,0,sizeof e);
        memset(ne,0,sizeof(ne));
        memset(match,0,sizeof match);
        for(int i=1;i<=p;i++)
        {
            int a, b;
            cin>>a>>b;
            add(a,b);
        }
        int res = 0; 
    for(int i=1; i<=n1;  i++) 
    {
        memset(st,false,sizeof(st)); 
        if(find(i)) res++;  
    } 
        cout << res<<endl;

    }

}

回文子序列

马拉车算法(吴老师的分析)

图片说明
• 对于p[i],如果i<mx,设j是i关于id对称点,如图所示,则基于以下三
种情况,可以求出p[i]的值:
这一点的话细心分析!就可以理解了
• (1)以j为中心的回文串有一部分在以id为中心的回文串之外。因为mx
是以id为中心的最长回文的右边界,所以以i为中心的回文串不可能会
有字符在以id为中心的回文串之外;否则mx就不是以id为中心的最长回
文的右边界。所以,在这种情况下,p[i]=mx–i。
• (2)以j为中心的回文串全部在以id为中心的回文串的内部,则
p[i]=p[j],而且p[i]不可能再增加。
• (3)以j为中心的回文串的左端正好与以id为中心的回文串的左端重合。
则p[i]=p[j]或p[i]=mx–i,并且p[i]还有可能会继续增加,即while
(s_new[i-p[i]]==s_new[i+p[i]]) p[i]++;
• 所以,if (i < mx) p[i] = min(p[2 * id - i], mx- i);其中2*id - i为i关于id的
对称点,即上面的j点,而p[j]表示以j为中心的最长回文半径,因此可
以利用p[j]来加快求解p[i]。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#define M 1000010
using namespace std;
char str[M],str_new[2*M]; //原串和辅助串
int p[2*M],len; //p[i]表示以第 i 个字符为中心的最长回文的半径;字符串长度 len
void init() { //构造辅助串
    len=strlen(str); //计算字符串长度
    str_new[0]='@'; //辅助串的首字符
    str_new[1]='#'; //辅助串的间隔字符
    for(int i=0; i<len; i++) { //逐个字符地构造辅助串
        str_new[i*2+2]=str[i];
        str_new[i*2+3]='#';
    }
    str_new[len*2+2]='$'; //辅助串的尾字符
}
void Manacher() { //计算和输出最长回文的半径
    memset(p,0,sizeof(p)); //p[i]表示以第 i 个字符为中心的最长回文的半径,所有最长回文的半径初始化为 0
    int mx=0,di,ans=0; //以 id 为中心的最长回文的右边界为 mx,即 mx=id+p[id],mx 和最长回文的长度 ans 初始化为 0
    for(int i=1; i<len*2+2; i++) { //枚举每一个可能的中心字符
        if(mx>i) //根据 i 位置在 mx 位置的左侧还是右侧,调整以最长回文的半径的最长回文半径的初始值
            p[i]=min(mx-i,p[di*2-i]);
        else
            p[i]=1;
        for(; str_new[i-p[i]]==str_new[i+p[i]]; p[i]++); //以 i 位置为中心计算最长回文半径 p[i]
        if(p[i]+i>mx) //若以 i 为中心的右边界大于 mx,则中心 id 调整为 i,重新计算右边界 mx
            mx=p[i]+i,di=i;
        ans=max(ans,p[i]); //调整最长回文的半径
    }
    printf("%d\n",--ans); //输出最长回文的半径
}
int main() {
    int t=1; //测试用例编号初始化
    while(~scanf("%s",str)) { //反复输入字符串,直至输入"END"为止
        if(!strcmp(str,"END")) break;
        printf("Case %d: ",t++); //输出测试用例编号
        init(); //构造辅助串
        Manacher(); //计算和输出最长回文的半径
    }
}

hash 循环节

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
char s[1001000];/// 输入字符串 
int mod=10009;//模 
int len,k=131;///s的长度为len 
ll now;
ll hash[1001000];//hash[i] 存储以第i个字符为尾的前缀的散列值 
ll cal(int x,ll y)//计算和返回 y^x%mod 的结果值 
{
    ll re=1;//结果值初始化 
    while(x)//分析次幂x的没一个二进制位 
    {
        if(x&1) re=(re*y)%mod;//若当前位为1,则累乘当前位的权并取模 
        x>>=1;y=(y*y)%mod;        //次幂x右移一位 ,计算该位的权后并取模 
    }
    return re;                //返回结果值 
}
bool check(int x)            //若所有长度为x的相邻子串对应的散列函数值相等 ,则返回 true 负责返回false 
{
    ll cc=cal(x,(ll)k);    //计算 k^x%mod 
    for(int i=(x<<1);i<=len;i+=x) //搜索 字符i(2*x<i<len).若任意长度i的子串S_i-x+1...i的散列值不等于长度为x的前缀的散列值,则返回false,否则返回true 
    {
        if((hash[i]-(hash[i-x]*cc)%mod+mod)%mod!=hash[x])
        {
            return false;
        }
    }
    return true;
}
int main()
{
    while(1)
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        if(len==1 && s[1]=='.')
        {
            return 0;
        }
        for(int i=1;i<=len;i++)/// 计算所有前缀的散列值 
        {
            hash[i]=(hash[i-1]*k+s[i])%mod;
        }
        for(int i=1;i<=len;i++)    /// 枚举可能的子串长度 
        {
            if(len%i==0 && check(i)) //若s能过划分出长度i的子串且所有相邻子串的 
            {
                printf("%d\n",len/i);
                break;
            }
        }
    }
}

哈希模板

#include <cstdio>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std;
const LL base1=131;
const LL base2=131;
const LL mod1=1e9+7;
const LL mod2=1e9+9;
char ss[10][10010];
struct node{
    LL hash1,hash2;
}a[10011];
LL make_hash1(char s[])
{
    LL ans=0;
    for(int i=0;i<strlen(s);i++)
     ans=(ans*base1+(LL)(s[i]))%mod1;
    return ans;
}
LL make_hash2(char s[])
{
    LL ans=0;
    for(int i=0;i<strlen(s);i++)
     ans=(ans*base2+(LL)(s[i]))%mod2;
    return ans;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     scanf("%s",ss[i]),a[i].hash1=make_hash1(ss[i]),a[i].hash2=make_hash2(ss[i]);

    cout<<"***********"<<endl;
    for(int i=1;i<=n;i++)
    {
        cout<<ss[i]<<" "<<a[i].hash1<<" "<<a[i].hash2<<endl;
    }
    return 0;
}//求子串哈希值hash=((hash[r]−hash[l−1]∗mod^(r−l+1))%mod+mod)%mod

KMP模板

#include<bits/stdc++.h>
using namespace std;
const int Maxn=1000005;
char s1[Maxn],s2[Maxn];
int l1,l2,fail[Maxn];
void Fail(){//构造fail数组
    for(int i=2,j=0;i<=l2;++i){
        while(j&&s2[j+1]!=s2[i])j=fail[j];
        if(s2[j+1]==s2[i])++j;
        fail[i]=j;
    }
}
void Match(){
    for(int i=1,j=0;i<=l1;++i){
        while(j&&s2[j+1]!=s1[i])j=fail[j];
        if(s2[j+1]==s1[i])++j;
        if(j==l2){
            //找到一个串,自行维护
            j=fail[j];
        }
    }
}
int main(){
    scanf("%s%s",s1+1,s2+1);
    l1=strlen(s1+1);
    l2=strlen(s2+1);
    Fail();
    Match();
    return 0;
}

凸包

#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<ll,ll> pii;
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
const int maxn=2e5+1010;
const double pi=acos(-1.0);
#define inf 0x3f3f3f3f
#define sf scanf
#define pf printf
const int mod=998244353;
const int MOD=10007;

struct point {
    double x,y;
    point(){

    }
    point(int a,int b){
        x=a;y=b;
    }
    point operator - (const point b)const {
        return point(x-b.x,y-b.y);
    }
    double operator * (const point b)const {
        return x*b.x+y*b.y;
    }
}point1;

point p[maxn];
int top=1,n,m,res[maxn];
bool cmp(point a,point b){
    if(a.x!=b.x) return  a.x<b.x;
    return a.y<b.y;
}
double dis(point a,point b){///求两点的距离
    return sqrt((a-b)*(a-b));
}
bool multi(point a,point b,point c){
    return (b.x - a.x)*(c.y - a.y) >= (c.x - a.x)*(b.y - a.y);
}
void Graham(int n){
    int i,len;
    sort(p,p+n,cmp);
    if(n==0) return; res[0]=0;
    if(n==1) return; res[1]=1;
    if(n==2) return; res[2]=2;
    for(int i=2;i<n;i++){
        while(top&&multi(p[i],p[res[top]],p[res[top-1]])) top--;
        res[++top]=i;
    }
    len=top;
    res[++top]=n-2;
    for(int i=n-3;i>=0;i--){
        while(top!=len&&multi(p[i],p[res[top]],p[res[top-1]])) top--;
        res[++top]=i;
    }
}

int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        sf("%lf%lf",&p[i].x,&p[i].y);
    Graham(n);
    double sum=0.0;
    for(int i=0;i<top-1;i++){
        sum+=fabs(p[res[i]].x*p[res[i+1]].y-p[res[i]].y*p[res[i+1]].x);
    }
    sum+=fabs(p[res[top-1]].x*p[res[0]].y-p[res[top-1]].y*p[res[0]].x);
    sum=sum/2;
    pf("%d\n",int(sum/50));
    return 0;
}

素数筛

int pri[N+9>>1],now;
bool vis[N+9];
void init(){
    for(int i=2;i<=N;i++){
        if(!vis[i])pri[++now]=i;
        for(int j=1;j<=now&&pri[j]*i<=N;j++){
            vis[pri[j]*i]=1;
            if(i%pri[j]==0)break;
        }
    }
}
全部评论

相关推荐

08-01 15:05
中南大学
点赞 评论 收藏
分享
Twilight_m...:表格简历有点难绷。说说个人看法: 1.个人基本情况里好多无意义信息,什么婚姻状况、健康状况、兴趣爱好、户口所在地、身份证号码、邮政编码,不知道的以为你填什么申请表呢。 2.校内实践个人认为对找工作几乎没帮助,建议换成和测开有关的项目,实在没得写留着也行。 3.工作经历完全看不出来是干什么的,起码看着和计算机没啥关系,建议加强描述,写点你在工作期间的实际产出、解决了什么问题。 4.个人简述大而空,看着像AI生成,感觉问题最大。“Python,C,C++成为我打造高效稳定服务的得力工具”、“我渴望凭借自身技术知识与创新能力,推动人工智能技术的应用发展,助力社会实现智能化转型”有种小学作文的美感。而且你确定你个人简述里写的你都会嘛?你AI这块写的什么“深入研究”,发几篇顶会的硕博生都不一定敢这么写。而且你AI这块的能力和软测也完全无关啊。个人简述建议写你对哪些技术栈、哪些语言、哪些生产工具的掌握,写的有条理些,而且最好是和测开强相关的。
点赞 评论 收藏
分享
08-01 16:33
门头沟学院 Java
挂掉了,我好难受,求安慰
投递三奇智元机器人科技有限公司等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务