目录
本篇文章主要是Mangata平时写代码所用到的代码模板库,如有不对请在评论区指出
先放一个我的 常数优化的博客: 传送门
再放一个我的 代码格式博客: 传送门
快读快输模板
有些题目可能会丧心病狂的卡常,这个时候我们可以使用快读快输这种方法优化我们程序的常数。
/*
作者:Mangata
int的快读快输
*/
#include<cstdio>
inline int read()//快读
{
int x = 0;
bool fg = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') fg = 0;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
if(fg)
return x;
return ~(x - 1);
}
inline int write(int x)//快输
{
if(x < 0)
{
x = ~(x - 1);
putchar('-');
}
int s[20];
int top = 0;
while(x)
{
s[++top] = x%10;
x /= 10;
}
if(!top) s[++top] = 0;
while(top)
putchar(s[top--] + '0');
}
int main()
{
while(1){
int n = read();
write(n);
putchar('\n');
}
return 0;
}
_int128模板
能够存储128位2进制的数(算是微大数)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline void scan(__int128 &x)//输入
{
x = 0;
int f = 1;
char ch;
if((ch = getchar()) == '-') f = -f;
else x = x*10 + ch-'0';
while((ch = getchar()) >= '0' && ch <= '9')
x = x*10 + ch-'0';
x *= f;
}
inline void print(__int128 x) {
if(x < 0) {
x = -x;
putchar('-');
}
if(x > 9)
print(x/10);
putchar(x % 10 + '0');
}
int main()
{
__int128 a,b,c;
scan(a);
scan(b);
c = a+b;
print(c);
return 0;
}
二分模板
二分的模板有很多,最好记住并使用一种二分模板就行,这是Mangata最爱的二分模板
/*
作者:Mangata
二分模板返回大于x的第一个位置
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;
int a[N],n,q;
int find(int l,int r,int key)//l为-1,r为数组长度
{
while(l + 1 < r)
{
int mid = l + r>>1;
if(a[mid] <= key)
l = mid;
else
r = mid;
}
return r;//返回大于Key的第一个位置
}
int main()
{
int k;
scanf("%d%d",&n,&q);
for(int i = 0; i < n; ++i)
scanf("%d",&a[i]);
for(int i = 0; i < q; ++i)
{
scanf("%d",&k);
printf("%d\n",find(-1,n,k));
}
}
背包模板
目前只写三种常用的背包模板(01、完全、多重),主要也是其他的背包都比较抽象,不好直接写代码。
01背包
/*
作者:Mangata
01背包问题模板 滚动数组优化
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
int f[N];
int v[N],w[N];
int n,V;
int main()
{
scanf("%d%d",&n,&V);
for(int i = 1; i <= n; ++i) scanf("%d%d",&w[i],&v[i]);
for(int i = 1; i <= n; ++i)
{
for(int j = V; j >= w[i]; --j)
{
f[j] = max(f[j],f[j-w[i]]+v[i]);
}
}
printf("%d\n",f[V]);
return 0;
}
完全背包
/*
作者:Mangata
完全背包问题模板 滚动数组优化
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
int f[N];
int v[N],w[N];
int n,V;
int main()
{
scanf("%d%d",&n,&V);
for(int i = 1; i <= n; ++i) scanf("%d%d",&w[i], &v[i]);
for(int i = 1; i <= n; ++i)
{
for(int j = w[i]; j <= V; ++j)
{
f[j] = max(f[j], f[j-w[i]]+v[i]);
}
}
printf("%d\n",f[V]);
return 0;
}
多重背包
/*
作者:Mangata
多重背包问题模板 二进制枚举优化
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
int f[N];
int v[N],w[N],p[N];
int n,V;
int main()
{
scanf("%d%d",&n,&V);
for(int i = 1; i <= n; ++i) scanf("%d%d%d",&w[i],&v[i],&p[i]);
for(int i = 1; i <= n; ++i)
{
int num = min(p[i],V/w[i]);
for(int k = 1; num > 0; k <<= 1)
{
if(k > num)
k = num;
num -= k;
for(int j = V; j >= w[i]*k; --j)
f[j] = max(f[j], f[j-w[i]*k]+v[i]*k);
}
}
printf("%d\n", f[V]);
return 0;
}
Manacher模板
Manacher算法主要应用于回文字符串上面,通过字符串翻倍我们能很轻松的通过对称的关系找到子字符串的回文长度。
/*
作者:Mangata
manacger模板
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
#define N 210005
char S[N],str[N*2+5];
int len1,len2,ans,p[N*2+5];
void init() {//数组初始化,即数组长度翻倍
str[0] = '$';//为了防止数组越界
str[1] = '#';
for(int i = 0; i < len1; ++i){
str[i * 2 + 2] = S[i];
str[i * 2 + 3] = '#';
}
len2 = len1 * 2 + 2;
str[len2] = '*';
}
void manacher() {//manacher
init();
int id = 0,mx = 0;//mx表示的是当前计算回文串最右边字符的最大值
for(int i=0; i < len2; ++i){
p[i]=mx > i?min(p[id*2-i],mx-i) : 1;//p[i]=mx>i?min(p[id*2-i],m-i):1;
for(; str[i+p[i]] == str[i-p[i]]; p[i]++);//如果字符串对称则对称长度增加
if(p[i]+i > mx)//如果大于当前的最大长度则更新
mx = p[i]+i, id = i;
}
}
int main() {
while(~scanf("%s",S)){
len1 = strlen(S);
manacher();
int maxlen = 0;
for(int i = 0; i < len2; ++i)
maxlen = max(p[i], maxlen);
printf("%d\n",maxlen-1);
}
return 0;
}
KMP模板
KMP是一个字符串匹配算法,通过此算法能达到线性的匹配时间复杂度,通过对字串求next数组,让我们字符串不回溯(或者说是减少步骤)
/*
作者:Mangata
manacger模板
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
int nextt[N];
char S[N],T[N];
int len1,len2;
void get_next() {//获取next数组
int i = 0,j = -1;
nextt[0] = -1;//很重要
while(i < len1){
if(j == -1 || S[i] == S[j])
nextt[++i] = ++j;
else
j = nextt[j];
}
}
int kmp(){//返回的是子串在主串中存在的次数
int cnt = 0;//表示的是子串在主串中存在的次数
int i = 0,j = 0;
get_next();
while(i < len2)
{
if(j == -1 || T[i] == S[j])
i++,j++;
else
j = nextt[j];
if(j == len1)//表示的是子串在主串中存在的次数
j=0,cnt++;//子串指针归零
}
return cnt;
}
int main(){
while(~scanf("%s",T))
{
if(T[0]=='#')
break;
scanf("%s",S);
len1 = strlen(S);
len2 = strlen(T);
printf("%d\n",kmp());
}
return 0;
}
并查集
并查集是一种集合数据结构,通过并查集我们可以快速查询两个元素是否是一个集合,下面是Mangata常用的板子
/*
作者:Mangata
路径压缩并查集
*/
#include<cstdio>
const int N = 10005;//节点数
int fa[N],n;
void init(int len) {//初始化,先让每个位置的父节点等于自身
for(int i=0; i<=n; ++i)
fa[i] = i;
}
int find(int x) {//查找x的祖先节点
int t = x;//路径压缩
while(t != fa[t])
t = fa[t];
while(x != fa[x]) {
int temp = fa[x];
fa[x] = t;
x = temp;
}
return x;
}
void merge(int a,int b) {//将x和y合并
a=find(a),b=find(b);
if(a != b) {
fa[b] = a;
n--;
}
}
int main()
{
int t,m,a,b;
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&m);
init(n);
for(int i = 1; i <= m; ++i) {
scanf("%d%d",&a,&b);
merge(a,b);
}
printf("%d\n",n);//输出不同类别的总数目
}
return 0;
}
GCD
GCD的四种方式,辗转相除法和更相减损法的循环写法和递归写法,但是一般来说要用gcd,直接调用__gcd函数就可
/*
作者:Mangata
GCD的四种写法
*/
#include<cstdio>
int gcd_1(int a,int b){//辗转相除法 循环写法
while(b > 0) {
int t = a%b;
a = b;
b = t;
}
return a;
}
int gcd_2(int a,int b) {//辗转相除法 递归写法
return b?gcd_2(b,a%b) : a;
}
int gcd_3(int a,int b) {//更相减损法 递归写法
if(a == 0)
return b;
if(b == 0)
return a;
if(a == b)
return a;
if(a > b)
return gcd_3(a-b,b);
if(a < b)
return gcd_3(b-a,a);
}
int gcd_4(int a,int b) {//更相减损法 循环写法
if(a == 0)
return b;
if(b == 0)
return a;
while(a != b) {
if(a > b)
a -= b;
else
{
int t = a;
a = b - a;
b = t;
}
}
return a;
}
int main()
{
int a,b;
while(~scanf("%d%d",&a,&b))
printf("gcd_1=%d\ngcd_2=%d\ngcd_3=%d\ngcd_4=%d\n",gcd_1(a,b),gcd_2(a,b),gcd_3(a,b),gcd_4(a,b));
}
拓展GCD
关于拓展GCD,我觉得比较核心的是贝祖定理,ax+by=gcd(a,b),方便我们求解不定方程,板子选取的题目是青蛙的约会
青蛙的约会
#include<cstdio>
#include<algorithm>
#define ll long long
//返回的是GCD(a,b)
ll extgcd(ll a, ll b, ll &x0, ll &y0) {//注意这里是引用
if (!b) {
x0 = 1, y0 = 0;
return a;
}
ll d = extgcd(b, a%b, x0, y0);
ll t = x0;
x0 = y0;
y0 = t - a / b * y0;
return d;
}
int main()
{
ll x,y,m,l,x0,y0,n;
while(~scanf("%lld%lld%lld%lld%lld",&x, &y, &m, &n, &l)) {
x0 = 0, y0 = 0;
ll c = x - y;
ll a = n - m;
if(a < 0) {
a = -a;
c = -c;
}
ll d = extgcd(a, l, x0, y0);
if (c % d)
puts("Impossible");
else
printf("%lld\n",(c/d*x0 % (l/d) + l/d) % (l/d));
}
return 0;
}
素数判断
朴素素数判断
/*
作者:Mangata
朴素素数判断模板
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
bool is_prime(int x) {
if(x == 0 || x == 1)
return false;
for(int i = 2; i*i <= x; ++i) {
if(x % i == 0)
return false;
}
return true;
}
int main()
{
while(~scanf("%d",&x)) {
if (is_prime(x))
puts("Yes");
else
puts("No");
}
return 0;
}
埃式筛
/*
作者:Mangata
埃式筛模板
*/
//时间复杂度 :O(n*log(log(n)))
#include<cstdio>
#include<algorithm>
#include<cstring>
const int N = 10000005;
bool vis[N] = {true,true};//初始化
int main()
{
int n,x;//离线处理
for(int i = 2; i*i <= N; ++i) {
if (!vis[i]) {
for(int j = i*2; j <= N; j += i) //把素数的倍数筛掉
vis[j] = true;
}
}
while(~scanf("%d",&x)) {
if (vis[x])
puts("No");
else
puts("Yes");
}
return 0;
}
欧拉筛
/*
作者:Mangata
欧拉筛模板
*/
//时间复杂度为 O(n)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
const int N = 10000005;
int prime[N];
bool vis[N];
void get_prime() {
memset(vis,true,sizeof vis);
memset(prime,0,sizeof prime);
vis[0] = vis[1] = false;
for(int i = 2; i <= N; ++i) {
if (vis[i]) {
prime[++prime[0]] = i;
}
for(int j = 1;j <= prime[0] && i*prime[j] <= N; ++j) {
vis[i * prime[j]] = false;
if (i % prime[j] == 0) //避免重复筛选
break;
}
}
}
int main()
{
int n;
get_prime();
while(~scanf("%d",&n)) {
if (vis[n])
puts("Yes");
else
puts("No");
}
return 0;
}
最小生成树
kruskal
最小生成数基于并查集的写法
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 205
struct node{
int from,to;
long long cost;
}E[N*N];
int fa[N],n,m;
bool cmp(node a,node b){
return a.cost<b.cost;
}
int find(int x){
int k = x;
while(k != fa[k])
k = fa[k];
while(x != fa[x]) {
int t =fa[x];
fa[x] = k;
x = t;
}
return x;
}
void init(){
for(int i = 1; i <= n; ++i)
fa[i] = i;
}
int same(int x,int y){
return find(x) == find(y);
}
void merge(int a,int b){
a = find(a);
b = find(b);
if(a != b)
fa[b] = a;
}
long long kruskal(){
long long ans = 0;
sort(E+1,E+m+1,cmp);
for(int i = 1; i <= m; ++i){
if(same(E[i].from,E[i].to))
continue;
merge(E[i].from,E[i].to);
ans += E[i].cost;
}
return ans;//返回的是最小生成树的代价
}
int main(){
int u,v,w;
while(~scanf("%d%d",&m,&n)&&m)
{
init();
for(int i = 1; i <= m; ++i){
scanf("%d%d%d",&u,&v,&w);
E[i].from = u,E[i].to = v,E[i].cost = w;
}
long long k = kruskal();
int loc = find(1);
for(int i = 2; i <= n; ++i)
if(find(i) != loc){
k = -1;
break;
}
if(k == -1)
puts("?");
else
printf("%lld\n",k);
}
return 0;
}
prim
最小生成树基于prim的写法
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define P pair<int, int>
#define INF 0x3f3f3f3f
const int N = 1005;
int mp[N][N];
bool vis[N];
int dis[N];
int n,m;
//返回的是一个生成树每条边的和
int prim(int s) {
for(int i = 1; i <= n ; ++i) {
dis[i] = INF;
vis[i] = false;
}
dis[s] = 0;
int ans = 0;
while(true) {
int v = -1;
for(int u = 1; u <= n; ++u) {
if(!vis[u] && (v == -1 || dis[u] < dis[v])) {
v = u;
}
}
if(v == -1)
break;
vis[v] = true;
ans += dis[v];
//松弛操作 更新最短路径
for(int u = 1; u <= n; ++u) {
dis[u] = min(dis[u], mp[u][v]);
}
}
return ans;
}
int main()
{
int u,v,cost;
while(~scanf("%d%d",&m, &n) && m) {
memset(mp,INF,sizeof mp);
for(int i = 0; i < m; ++i) {
scanf("%d%d%d",&u, &v, &cost);
if(mp[u][v] > cost)
mp[u][v] = mp[v][u] = cost;
}
int ans = prim(1);
if(ans < INF) {
printf("%d\n",ans);
} else {
puts("?");
}
}
return 0;
}
快速幂&快速乘
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
//快速幂
ll quick_pow(ll x, ll n, ll mod) {
ll res = 1;
while(n > 0) {
if(n & 1) res = (res * x)% mod;
x = (x * x) % mod;
n >>= 1;
}
return res;
}
//快速乘
ll quick_pow2(ll x, ll n, ll mod) {
ll res = 0;
x%= mod;
n%= mod;
while(n) {
if(n & 1) {
res = (res + x) % mod;
}
x = (x%mod + x%mod) % mod;
n >>= 1;
}
return res%mod;
}
int main()
{
ll a,b,c,n;
// while(~scanf("%lld%lld%lld",&a,&b,&c)) {
// printf("%lld\n",quick_pow(a,b,c));
// }
ll key,temp;
while(~scanf("%lld%lld",&n,&c)) {
scanf("%lld",&key);
key %= c;
for(ll i = 1; i < n; ++i) {
scanf("%lld",&temp);
key = quick_pow2(key,temp,c);
}
printf("%lld\n",key);
}
return 0;
}
树状数组
二维单点修改,区间查询
例题:传送门
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 5000;
ll tree[N][N<<2];
int n,m;
int lowbit(int x) {
return -x & x;
}
inline void updata(int x,int y,int k) {
while(x <= n) {
int temp = y;
while(y <= m) {
tree[x][y] += k;
y += lowbit(y);
}
x += lowbit(x);
y = temp;
}
}
inline ll get(int x,int y) {
ll ans = 0;
while(x) {
int temp = y;
while(y) {
ans += tree[x][y];
y -= lowbit(y);
}
x -= lowbit(x);
y = temp;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
int a,b,c,d,op;
while(~scanf("%d",&op)) {
if(op == 1) {
scanf("%d%d%d",&a,&b,&c);
updata(a,b,c);
}
else {
scanf("%d%d%d%d",&a,&b,&c,&d);
printf("%lld\n",get(c,d)-get(c,b-1)-get(a-1,d) + get(a-1,b-1));
}
}
return 0;
}
线段树
单点修改,区间查询
例题:HDU1754
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 2000005;
int n,m;
int a[N],tree[N << 2];
void push_up(int k) {
tree[k] = max(tree[k<<1],tree[k<<1|1]);
}
void build(int k, int l,int r) {
if(l == r) {
tree[k] = a[l];
}
else {
int mid = l + ((r-l)>>1);
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
push_up(k);
}
}
void updata(int p,int v,int l,int r,int k) {
if(l == r) {
a[p] += v, tree[k] += v;
}
else {
int mid = l + ((r-l)>>1);
if(p <= mid) {
updata(p,v,l,mid,k<<1);
}
else {
updata(p,v,mid+1,r,k<<1|1);
}
push_up(k);
}
}
int query(int L, int R,int l,int r,int k) {
if(L <= l && R >= r) {
return tree[k];
}
else {
int ans = -INF;
int mid = l+r >>1;
if(L <= mid) {
ans = max(ans,query(L,R,l,mid,k<<1));
}
if(R > mid) {
ans = max(ans,query(L,R,mid+1,r,k<<1|1));
}
return ans;
}
}
int main()
{
while(~scanf("%d%d",&n,&m)) {
for(int i = 1;i <= n; ++i) {
scanf("%d",&a[i]);
}
build(1,1,n);
char op;
int l,r;
while(m--) {
cin>>op;
if(op == 'Q') {
scanf("%d%d",&l,&r);
printf("%d\n",query(l,r,1,n,1));
}
else if(op == 'U'){
scanf("%d%d",&l,&r);
updata(l,r-a[l],1,n,1);
}
}
}
return 0;
}
区间更新、区间查询
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6+10;
ll tree[N<<2],tag[N<<2];
ll a[N];
inline ll ls(ll p) {return p<<1;}
inline ll rs(ll p) {return p<<1|1;}
void push_up_min(ll p) {//向上更新操作
tree[p] = min(tree[ls(p)],tree[rs(p)]);
}
void build(ll p,ll l,ll r) {//建树操作
if(l == r) {
tree[p] = a[l];
return;
}
ll mid = l + r >> 1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up_min(p);
}
inline void push_down(ll p,ll l,ll r) {//向下更新操作
if(tag[p]) {
tag[ls(p)] += tag[p];
tag[rs(p)] += tag[p];
tree[ls(p)] += tag[p];
tree[rs(p)] += tag[p];
tag[p] = 0;
}
//更新两个儿子节点的tag和tree值
}
void updata(ll L ,ll R ,ll l ,ll r ,ll p ,ll k) {//[L,R]是我们想要修改的区间,
if(L <= l && R >= r) { //[l,r]是我们目前搜索到的区间
tree[p] += k;
tag[p] += k;
return;
}
push_down(p,l,r);//回溯前往下传递更新
ll mid = l + r >> 1;
if(L <= mid) updata(L,R,l,mid,ls(p),k);//如果我们想修改的区间比当前的中间节点小or等于那么就查询
if(R > mid) updata(L,R,mid+1,r,rs(p),k);//如果我们想修改的区间比当前中间节点大那么就查询
push_up_min(p);//回溯后往上传递更新
}
ll query(ll L, ll R,ll l,ll r,ll p) {//[L,R]是我们要查询的区间,[l,r]是当前节点存储的区间
ll res = 0x3f3f3f3f3f3f3f3f;
if(L <= l && R >= r) return tree[p];
ll mid = l + r >> 1;
push_down(p,l,r);
if(L <= mid)
res = min(res,query(L,R,l,mid,ls(p)));
if(R > mid)
res = min(res,query(L,R,mid+1,r,rs(p)));
return res;
}
int n,m;
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n; ++i) {
scanf("%lld",&a[i]);
}
build(1,1,n);
int q;
ll l,r,k;
int fg = 0;
for(int i = 1;i <= m; ++i) {
scanf("%lld%lld%lld",&k,&l,&r);
if(fg) continue;
if(query(l,r,1,n,1) >= k)
updata(l,r,1,n,1,-k);
else fg = i;
}
if(fg) {
printf("-1\n%d\n",fg);
}
else puts("0");
return 0;
}
最短路
迪杰斯特拉(堆优化+链式前向星)
#include<cstdio>
#include<cstring>
#include<queue>//
using namespace std;
const int N=2e5+5;//数据范围
struct edge{//存储边
int u,v,w,next;//u为起点,v为终点,w为权值,next为前继
};
edge e[N];
int head[N],dis[N],n,m,s,cnt;//head为链中最上面的,dis表示当前答案,n为点数,m为边数,s为起点,cnt记录当前边的数量
bool vis[N];//vis表示这个点有没有走过
struct node{
int w,to;//w表示累加的权值,to表示到的地方
bool operator <(const node &x)const{//重载“<”号
return w>x.w;
}
};
priority_queue<node>q;//优先队列(堆优化)
void add(int u,int v,int w){
++cnt;//增加边的数量
e[cnt].u=u;//存起点
e[cnt].v=v;//存终点
e[cnt].w=w;//存权值
e[cnt].next=head[u];//存前继
head[u]=cnt;//更新链最上面的序号
}//链式前向星(加边)
void Dijkstra(){
memset(dis,127,sizeof(dis));//初始化,为dis数组附一个极大值,方便后面的计算
dis[s]=0;//起点到自己距离为0
q.push(node{0,s});//压入队列
while(!q.empty()){//队列不为空
node x=q.top();//取出队列第一个元素
q.pop();//弹出
int u=x.to;//求出起点
if(vis[u]) continue;//已去过就不去了
vis[u]=true;//标记已去过
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;//枚举终点
if(dis[v]>dis[u]+e[i].w){//若中转后更优,就转
dis[v]=dis[u]+e[i].w;//更新
q.push(node{dis[v],v});//压入队列
}
}
}
}
int main(){
int u,v,w = 1;
s = 1;
scanf("%d%d",&n,&m);//输入
for(int i=1;i<=m;++i){
scanf("%d%d",&u,&v);
add(u,v,w);
add(v,u,w);
}
Dijkstra();//DJ
printf("%d\n",dis[n]-1);//输出1-n的最短路
return 0;
}
组合数模板
ll qpow(ll a,ll b,ll c) {
ll ans = 1;
while(b) {
if(b & 1) ans = (ans * a) % c;
b >>= 1;
a = (a * a) % c;
}
return ans;
}
ll inv(ll x,ll c) {//逆元
return qpow(x,c-2,c);
}
ll C(ll a,ll b, ll c) {//组合数C(b,a) a>=b
ll ans = 1;
for(int i = 1;i <= b; ++i) {
ans = ans * (a-i+1) % c;
ans = ans * inv(i,c) % c;
}
return ans;
}
N圆面积并&&交
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef vector <int> VI;
const int INF = 0x3f3f3f3f;
const double eps = 1e-10;
const int MOD = 100000007;
const int MAXN = 1000010;
const double PI = acos(-1.0);
#define sqr(x) ((x)*(x))
const int N = 1010;
double area[N];
int n;
int dcmp(double x){
if (x < -eps) return -1;
else return x > eps;
}
struct cp{
double x, y, r, angle;
int d;
cp() {}
cp(double xx, double yy, double ang = 0, int t = 0){
x = xx;
y = yy;
angle = ang;
d = t;
}
void get(){
scanf("%lf%lf%lf", &x, &y, &r);
d = 1;
}
} cir[N], tp[N * 2];
double dis(cp a, cp b){return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));}
double cross(cp p0, cp p1, cp p2){return (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x);}
int CirCrossCir(cp p1, double r1, cp p2, double r2, cp &cp1, cp &cp2){
double mx = p2.x - p1.x, sx = p2.x + p1.x, mx2 = mx * mx;
double my = p2.y - p1.y, sy = p2.y + p1.y, my2 = my * my;
double sq = mx2 + my2, d = -(sq - sqr(r1 - r2)) * (sq - sqr(r1 + r2));
if (d + eps < 0) return 0;
if (d < eps) d = 0;
else d = sqrt(d);
double x = mx * ((r1 + r2) * (r1 - r2) + mx * sx) + sx * my2;
double y = my * ((r1 + r2) * (r1 - r2) + my * sy) + sy * mx2;
double dx = mx * d, dy = my * d;
sq *= 2;
cp1.x = (x - dy) / sq;
cp1.y = (y + dx) / sq;
cp2.x = (x + dy) / sq;
cp2.y = (y - dx) / sq;
if (d > eps) return 2;
else return 1;
}
bool circmp(const cp& u, const cp& v){return dcmp(u.r - v.r) < 0;}
bool cmp(const cp& u, const cp& v){
if (dcmp(u.angle - v.angle)) return u.angle < v.angle;
return u.d > v.d;
}
double calc(cp cir, cp cp1, cp cp2){
double ans = (cp2.angle - cp1.angle) * sqr(cir.r)
- cross(cir, cp1, cp2) + cross(cp(0, 0), cp1, cp2);
return ans / 2;
}
void CirUnion(cp cir[], int n){
cp cp1, cp2;
sort(cir, cir + n, circmp);
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j)
if (dcmp(dis(cir[i], cir[j]) + cir[i].r - cir[j].r) <= 0)
cir[i].d++;
for (int i = 0; i < n; ++i){
int tn = 0, cnt = 0;
for (int j = 0; j < n; ++j){
if (i == j) continue;
if (CirCrossCir(cir[i], cir[i].r, cir[j], cir[j].r,cp2, cp1) < 2) continue;
cp1.angle = atan2(cp1.y - cir[i].y, cp1.x - cir[i].x);
cp2.angle = atan2(cp2.y - cir[i].y, cp2.x - cir[i].x);
cp1.d = 1;
tp[tn++] = cp1;
cp2.d = -1;
tp[tn++] = cp2;
if (dcmp(cp1.angle - cp2.angle) > 0) cnt++;
}
tp[tn++] = cp(cir[i].x - cir[i].r, cir[i].y, PI, -cnt);
tp[tn++] = cp(cir[i].x - cir[i].r, cir[i].y, -PI, cnt);
sort(tp, tp + tn, cmp);
int p, s = cir[i].d + tp[0].d;
for (int j = 1; j < tn; ++j){
p = s;
s += tp[j].d;
area[p] += calc(cir[i], tp[j - 1], tp[j]);
}
}
}
void solve(){
scanf("%d", &n);
double sum = 0;
for (int i = 0; i < n; ++i)cir[i].get();
memset(area, 0, sizeof(area));
CirUnion(cir, n);
for (int i = 1; i <= n; ++i) //去掉重复计算的
area[i] -= area[i + 1]; //area[i]为重叠了i次的面积
double tot = 0;//tot 为总面积
for(int i=1; i<=n; i++) tot += area[i];
// cout<<area[n]<<endl; //重叠了n次就是所求N圆相交面积
printf("%f\n", tot); //这句就是求总面积
}
int main(){
solve();
return 0;
}
/*
in
3
1 0 2
-1 0 2
0 1 2
Out
4.20324 相交面积
//22.929505 相并面积
*/
凸包&判凸包相交
#include<cstdio>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const double eps = 1e-10;
double dcmp(double x) {
if(fabs(x) < eps) return 0;
else return x < 0 ? -1 : 1;
}
struct Point {
double x, y;
Point(double x=0, double y=0):x(x),y(y) {}
};
typedef Point Vector;
Vector operator - (const Point& A, const Point& B) {
return Vector(A.x-B.x, A.y-B.y);
}
double Cross(const Vector& A, const Vector& B) {
return A.x*B.y - A.y*B.x;
}
double Dot(const Vector& A, const Vector& B) {
return A.x*B.x + A.y*B.y;
}
bool operator < (const Point& p1, const Point& p2) {
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}
bool operator == (const Point& p1, const Point& p2) {
return p1.x == p2.x && p1.y == p2.y;
}
bool SegmentProperIntersection(const Point& a1, const Point& a2, const Point& b1, const Point& b2) {
double c1 = Cross(a2-a1,b1-a1), c2 = Cross(a2-a1,b2-a1),
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(const Point& p, const Point& a1, const Point& a2) {
return dcmp(Cross(a1-p, a2-p)) == 0 && dcmp(Dot(a1-p, a2-p)) < 0;
}
// 点集凸包
// 如果不希望在凸包的边上有输入点,把两个 <= 改成 <
// 如果不介意点集被修改,可以改成传递引用
vector<Point> ConvexHull(vector<Point> p) {
// 预处理,删除重复点
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
int n = p.size();
int m = 0;
vector<Point> ch(n+1);
for(int i = 0; i < n; i++) {
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
int k = m;
for(int i = n-2; i >= 0; i--) {
while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
ch.resize(m);
return ch;
}
//点是否在凸包内
int IsPointInPolygon(const Point& p, const vector<Point>& poly) {
int wn = 0;
int n = poly.size();
for(int i=0; i<n; ++i) {
const Point& p1 = poly[i];
const Point& p2 = poly[(i+1)%n];
if(p1 == p || p2 == p || OnSegment(p, p1, p2)) return -1;//在边界上
int k = dcmp(Cross(p2-p1, p-p1));
int d1 = dcmp(p1.y - p.y);
int d2 = dcmp(p2.y - p.y);
if(k > 0 && d1 <= 0 && d2 > 0) wn++;
if(k < 0 && d2 <= 0 && d1 > 0) wn--;
}
if(wn != 0) return 1;
return 0;
}
//判断2个凸包是否相交
bool ConvexPolygonDisjoint(const vector<Point> ch1, const vector<Point> ch2) {
int c1 = ch1.size();
int c2 = ch2.size();
for(int i=0; i<c1; ++i)
if(IsPointInPolygon(ch1[i], ch2) != 0) return false;
for(int i=0; i<c2; ++i)
if(IsPointInPolygon(ch2[i], ch1) != 0) return false;
for(int i=0; i<c1; ++i)
for(int j=0; j<c2; ++j)
if(SegmentProperIntersection(ch1[i], ch1[(i+1)%c1], ch2[j], ch2[(j+1)%c2])) return false;
return true;
}
int main() {
int n, m;
int t;
scanf("%d%d", &n,&m);
vector<Point> P1, P2;
double x, y;
int ww;
for(int i = 0; i < n; i++) {
scanf("%lf%lf", &x, &y);
P1.push_back(Point(x, y));
}
for(int i = 0;i < m; ++i) {
scanf("%lf%lf", &x, &y);
P2.push_back(Point(x, y));
}
if(P1.size()==0||P2.size()==0){
printf("NO\n");
return 0;
}
if(ConvexPolygonDisjoint(ConvexHull(P1), ConvexHull(P2)))
printf("NO\n");
else
printf("YES\n");
return 0;
}
To Be Continue……