HYNUACMer算法模板
数论
常用数学公式................................................................................................................... 4
1 a^b%c..................................................................................................................... 4
2 矩阵的幂取模........................................................................................................ 4
3大数的前n位.......................................................................................................... 5
5 Fibonacci Number.................................................................................................... 5
6 Lucas Number.......................................................................................................... 5
7 Catalan Number....................................................................................................... 5
8 Stirling Number(Second Kind).................................................................................. 6
9 Bell Number............................................................................................................ 7
10 Stirling's Approximation.......................................................................................... 7
11 Sum of Reciprocal Approximation............................................................................ 7
12 Young Tableau........................................................................................................ 7
13 整数划分.............................................................................................................. 7
14 错排公式.............................................................................................................. 8
15 三角形内切圆半径公式......................................................................................... 8
16 三角形外接圆半径公式......................................................................................... 8
17 圆內接四边形面积公式......................................................................................... 8
18 基础数论公式....................................................................................................... 8
欧拉公式.......................................................................................................................... 8
容斥原理........................................................................................................................ 10
模线性方程组(完全平方数组数问题)............................................................................... 13
多重01背包问题............................................................................................................ 15
最小公倍数和最大公约数差值最小.................................................................................. 16
求素数因子..................................................................................................................... 18
跳跃素数问题................................................................................................................. 19
线性规划数论推理.......................................................................................................... 20
数论找规律..................................................................................................................... 21
矩阵模幂........................................................................................................................ 23
博弈论............................................................................................................................ 25
开特兰数大数处理.......................................................................................................... 26
大数模版........................................................................................................................ 28
大数加乘........................................................................................................................ 35
数据结构
并查集............................................................................................................................ 37
单调队列........................................................................................................................ 38
字典树............................................................................................................................ 39
平衡二叉树(SBT算法)................................................................................................ 41
排序二叉树..................................................................................................................... 44
1 BST树的建立与维护............................................................................................. 44
2 Defense Lines......................................................................................................... 45
堆................................................................................................................................... 47
1大顶堆.................................................................................................................. 47
2二分堆(binary)....................................................................................................... 48
3映射二分堆(mapped).............................................................................................. 48
树状数组........................................................................................................................ 49
1子段和.................................................................................................................. 49
2子阵和.................................................................................................................. 50
线段树............................................................................................................................ 50
1 RMQ问题(频繁出现的数值)............................................................................. 50
2点维护(在给定区间内动态维护最大连续和)....................................................... 52
3区间维护............................................................................................................... 57
4线段树求最小值.................................................................................................... 62
5 带循环移动的RMQ.............................................................................................. 66
计算机几何
最近点对........................................................................................................................ 69
最小包围圆..................................................................................................................... 71
求两个圆的交点.............................................................................................................. 73
求三角形外接圆圆心....................................................................................................... 75
求凸包............................................................................................................................ 76
凸包卡壳旋转求出所有对踵点、最远点对....................................................................... 78
凸包+旋转卡壳求平面面积最大三角................................................................................ 82
Pick定理........................................................................................................................ 84
求多边形面积和重心....................................................................................................... 86
蚁群退火算法................................................................................................................. 86
模拟退火........................................................................................................................ 88
用一个给定半径的圆覆盖最多的点.................................................................................. 90
最近圆对........................................................................................................................ 92
求两个圆的面积交.......................................................................................................... 95
判断线段相交................................................................................................................. 96
平面分成区域数.............................................................................................................. 97
图 论
网络流.......................................................................................................................... 101
1点离散化............................................................................................................. 101
2 EK算法.............................................................................................................. 103
3 ISAP算法........................................................................................................... 105
最小生成树................................................................................................................... 108
1 kruskal................................................................................................................ 108
2 prim.................................................................................................................... 110
3 最优生成树......................................................................................................... 111
4有向图最小生成树............................................................................................... 113
最短路径....................................................................................................................... 116
1 Dijkstra(邻接矩阵)........................................................................................... 116
2 flody................................................................................................................... 117
3最短路径spfa....................................................................................................... 118
4 Bellman_ford算法............................................................................................... 120
动态规划
数位DP........................................................................................................................ 121
一维动态规划............................................................................................................... 122
二维动态规划............................................................................................................... 123
概率DP........................................................................................................................ 128
其他杂例
三分............................................................................................................................. 129
1 基本三分............................................................................................................ 129
国际网络(深搜标程)................................................................................................. 131
双向广度搜索模拟........................................................................................................ 132
栈模拟ZZ的计算器...................................................................................................... 134
八数码.......................................................................................................................... 136
找路径上单元格最大难度值与最小值之和..................................................................... 138
字符串.......................................................................................................................... 140
递归遍历(约束条件)................................................................................................. 142
C++最值常量................................................................................................................ 143
类型转换...................................................................................................................... 143
String常用函数举例...................................................................................................... 144
C++常用头文件............................................................................................................. 145
数论
常用数学公式
1 a^b%c
LL PowMod(LL a,LL b,LL c)
{
LL ans=1;
while(b)
{
if(b&1) ans=a*ans%c;
a=(a%c)*(a%c)%c;
b/=2;
}
return ans;
}
2 矩阵的幂取模
struct Mat
{
int matrix[2][2];
};
Mat mat;
int f[40];
int n;
mat.matrix[0][0] = mat.matrix[0][1] = mat.matrix[1][0] = 1;
mat.matrix[1][1] = 0;
Mat mul (Mat a, Mat b)
{
int i, j, k;
Mat c;
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
{
c.matrix[i][j] = 0;
for (k = 0; k < 2; k++)
{
c.matrix[i][j] += a.matrix[i][k] * b.matrix[k][j];
c.matrix[i][j] %= 10000;
}
}
return c;
}
Mat solve (int m)
{
Mat mt;
if (m == 1) return mat;
if (m & 1) return mul (solve (m-1), mat);
else
{
mt = solve (m/2);
return mul (mt, mt);
}
}
3大数的前n位
对任意的大数为m,其结果ans=pow10(log10(m)-(int)log10(m))*pow10(n-1);
4 Fibonacci Numbers 的通项公式
f(n)=1/sqrt(5.0)(((1+sqrt(5.0))/2)^n+((1-sqrt(5.0))/2)^n);
5 Fibonacci Number
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610 …
Formula:
6 Lucas Number
1, 3, 4, 7, 11, 18, 29, 47, 76, 123...
Formula:
7 Catalan Number
1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012…
Formula:
Application:
将 n + 2 边形沿弦切割成 n个三角形的不同切割数
Sample:
n = 2;
n = 3;
n + 1个数相乘, 给每两个元素加上括号的不同方法数
Sample:
n = 2; (1 (2 3)), ((1 2) 3)
n = 3; (1 (2 (3 4))), (1 ((2 3) 4)) , ((1 2) (3 4)), ((1 (2 3)) 4), (((1 2) 3) 4)
n 个节点的不同形状的二叉树数(严《数据结构》P.155)
从n * n 方格的左上角移动到右下角不升路径数
Sample:
n = 2;
n = 3;
8 Stirling Number(Second Kind)
S(n, m)表示含n个元素的集合划分为m个集合的情况数
或者是n个有标号的球放到m 个无标号的盒子中, 要求无一为空, 其不同的方案数
Formula:
Special Cases:
9 Bell Number
n 个元素集合所有的划分数
Formula:
10 Stirling's Approximation
11 Sum of Reciprocal Approximation
EulerGamma = 0.57721566490153286060651209;
12 Young Tableau
Young Tableau(杨式图表)是一个矩阵, 它满足条件:
如果格子[i, j]没有元素, 则[i+1, j]也一定没有元素
如果格子[i, j]有元素a[i, j],则[i+1, j]要么没有元素, 要么a[i+1, j] > a[i, j]
Y[n]代表n个数所组成的杨式图表的个数
Formula:
Sample:
n = 3;
13 整数划分
将整数n分成k份, 且每份不能为空, 任意两种分法不能相同
1) 不考虑顺序
for(int p=1; p<=n ;p++)
for(int i=p; i<=n ;i++)
for(int j=k; j>=1 ;j--)
dp[i][j] += dp[i-p][j-1];
cout<< dp[n][k] <<endl;
2) 考虑顺序
dp[i][j] = dp[i-k][j-1]; (k=1..i)
3) 若分解出来的每个数均有一个上限m
dp[i][j] = dp[i-k][ j-1]; (k=1..m)
14 错排公式
15 三角形内切圆半径公式
16 三角形外接圆半径公式
17 圆內接四边形面积公式
18 基础数论公式
1) 模取幂
2) n的约数的个数
若n满足 , 则n的约数的个数为
欧拉公式
int eular(int n)
{
int i,res=n;
for(i=2;i*i<=n;i++){
if(n%i==0){
res=(res/i)*(i-1);
do n/=i;
while(n%i==0);
}
}
if(n!=1) res=(res/n)*(n-1);
return res;
}
/*题目:给出一个最简分数a/b,求在分母小于等于n的情况下,小于a/b且最接近a/b的最简分数,输出这个最简分数的分子;
找不到这样的最简分数的话则输出-1.
思路:假设这个分数为 x/i
则x/i > a/b 即x > a*i /b
暴力分母i 让x=a*i/b 如果 x与i不互质则x-- 否则就可以认为是一个小于a /b的分数
然后从这些分数中找到离a/b最小的即可*/
#include <stdio.h>
int num1[10011],num2[10011];
int Gcd(int a,int b){
int r;
if(!a || !b) return 0;
while(b!=0){
r=b;
b=a%b;
a=r;
}
return a;
}
int main()
{
int T;
int a,b,n,i;
int tmp,cnt,min_a,min_b;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&a,&b,&n);
cnt=0;
for(i=1; i<=n; i++) //暴力分母
{
tmp=a*i/b;//不能等于它 应该小于它 由于他是取整 如果可以整除的话则tmp-- 否则则不需要--了
if(a*i%b==0) tmp--;
while(Gcd(tmp,i)!=1 && tmp>0)tmp--;
if(tmp>0){
num1[++cnt]=tmp;//分子
num2[cnt]=i;//分母
}
}
if(cnt<1){
printf("-1\n");
continue;
}
min_a=num1[1];
min_b=num2[1];
for(i=2; i<cnt; i++){
if(num1[i]*min_b>num2[i]*min_a){
min_a=num1[i];
min_b=num2[i];
}
}
printf("%d\n",min_a);
}
return 0;
}
容斥原理
对于一个数F,设F = p1^e1*p2^e2...*pn^en 那么[1-N]内与其互质的数和与D = p1*p2*...pn是一致的,因为和F、D互质的数都是不含有他们的素因子的数。对于D这个数求[1-N]内有多少个与其互质的数就可用运用容斥定理来求解了。以6为例,首先计算出与2不互质的数,用等差数列公式能够计算出这些满足与2不互质数的和,接着就加上与3不互质的和,最后再减一次与6不互质的和即可。对于那些改变的数,由于数量不是很多,可以单独拿出来进行处理。
#include <iostream>
#include <cstdio>
#include <map>
#define ll long long
#include <cstring>
using namespace std;
const int N = 400010;
bool isprime[N];
map<int,int> mp;
int prime[34000],cnt;
int factor[30];//素数因子
void Prime() //素数打表
{
cnt=0;
memset(isprime,true,sizeof(isprime));
for(int i=2; i<N; i++)
{
if(isprime[i])
{
for(int j=i+i; j<N; j+=i)
isprime[j]=false;
prime[cnt++]=i;
}
}
}
int gcd(int a,int b)
{
if(b==0) return a;
return gcd(b,a%b);
}
ll finds(int x,int n,int p) //容斥原理
{
int t,i,j,num,d,m=1<<n;
ll ans=(ll)x*(x+1)/2;
for(i=1; i<m; i++)
{
t=i;
j=num=0;
d=1;
while(t)
{
if(t&1)
{
d*=factor[j];
num++;
}
j++;
t>>=1;
}
n=x/d;
if(num&1) ans-=(ll)d*(1+n)*n/2;
else ans+=(ll)d*(1+n)*n/2;
}
map<int,int>::iterator it;
for(it=mp.begin(); it!=mp.end(); it++) //处理被改变了的数
{
if(it->first>x) continue;
if(gcd(it->first,p)==1) ans-=it->first;
if(gcd(it->second,p)==1) ans+=it->second;
}
return ans;
}
int pri(int a) //求出a的素数因子
{
if(isprime[a])
{
factor[0]=a;
return 1;
}
int k=0,i;
for(i=0; i<cnt; i++)
{
if(a%prime[i]==0) factor[k++]=prime[i];
while(a%prime[i]==0) a/=prime[i];
if(a!=1&&isprime[a])
{
factor[k++]=a;
return k;
}
}
return k;
}
int main()
{
Prime();
int n,m,x,y,op,p,cs;
scanf("%d",&cs);
while(cs--)
{
scanf("%d%d",&n,&m);
mp.clear();
for(int i=0; i<m; i++)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d",&x,&y,&p);
int num=pri(p);
printf("%I64d\n",finds(y,num,p)-finds(x-1,num,p));
}
else
{
scanf("%d%d",&x,&p);
mp[x]=p;
}
}
}
return 0;
}
模线性方程组(完全平方数组数问题)
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 500 + 10;
const int maxp = 100;
int vis[maxn];
int prime[maxp];
int gen_primes(int n)//打出n以内的素因子表
{
int m = (int)sqrt(n+0.5);
memset(vis, 0, sizeof(vis));
for(int i = 2; i <= m; i++) if(!vis[i])
for(int j = i*i; j <= n; j+=i) vis[j] = 1;
int c = 0;
for(int i = 2; i <= n; i++) if(!vis[i])
prime[c++] = i;
return c;
}
typedef int Matrix[maxn][maxn];
// m个方程,n个变量
int rank(Matrix A, int m, int n)//高斯消元法
{
int i = 0, j = 0, k, r, u;
while(i < m && j < n) // 当前正在处理第i个方程,第j个变量
{
r = i;
for(k = i; k < m; k++)
if(A[k][j])
{
r = k;
break;
}
if(A[r][j])
{
if(r != i) for(k = 0; k <= n; k++) swap(A[r][k], A[i][k]);
// 消元后第i行的第一个非0列是第j列,且第u>i行的第j列均为0
for(u = i+1; u < m; u++) if(A[u][j])
for(k = i; k <= n; k++) A[u][k] ^= A[i][k];
i++;
}
j++;
}
return i;
}
Matrix A;
int main()
{
int m = gen_primes(500);
int T;
cin >> T;
while(T--)
{
int n, maxp = 0;
long long x; // 注意x的范围
cin >> n;
memset(A, 0, sizeof(A));
for(int i = 0; i < n; i++)
{
cin >> x;
for(int j = 0; j < m; j++) // 求x中的prime[j]的幂,并更新系数矩阵
while(x % prime[j] == 0)
{
maxp = max(maxp, j);
x /= prime[j];
A[j][i] ^= 1;
}
}
int r = rank(A, maxp+1, n); // 只用到了前maxp+1个素数
cout << (1LL << (n-r))-1 << endl; // 空集不是解,所以要减1
}
return 0;
}
多重01背包问题
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
int a[15], f[MAXN], V;
void ZeroOnePack(int F[], int C, int W)
{
for(int v = V; v >= C; v --)
F[v] = max(F[v], F[v - C] + W);
}
void CompletePack(int F[], int C, int W)
{
for(int v = C; v <= V; v ++)
F[v] = max(F[v], F[v - C] + W);
}
void MultiplePack(int F[], int C, int W, int M)
{
if(C * M >= V)
{
CompletePack(F, C, W);
return;
}
int k = 1;
while(k < M)
{
ZeroOnePack(F, k * C, k * W);
M -= k;
k <<= 1;
}
ZeroOnePack(F, C * M, W * M);
}
int main()
{
int t = 0;
while(true)
{
int total = 0;
for(int i = 1; i <= 10; i ++)
{
scanf("%d", &a[i]);
total += a[i] * i;
}
if(total == 0) break;
++ t;
if(total & 1) printf("#%d:Can't be divided.\n", t);
else
{
int target = total >> 1;
memset(f, 0, sizeof(f));
V = target;
for(int i = 1; i <= 10; i ++) MultiplePack(f, i, i, a[i]);
if(f[target] == target) printf("#%d:Can be divided.\n", t);
else printf("#%d:Can't be divided.\n", t);
printf("\n");
}
}
return 0;
}
最小公倍数和最大公约数差值最小
#include <stdio.h>
#include <string.h>
#define maxn 33333
char mark[maxn]; //标记某数是否为素数
int pn,prime[5000]; //素数个数,记录素数的数组
int p[10],n;
int best,bestl,bestr; //对乘积cd枚举分解得出的最优差值和最优分值方案
void makeprime()
{
int i,j;
pn=0; //初始化素数个数
memset(mark,0,sizeof(mark)); //初始化素数标记
for(i=2;i<maxn;i++)
{
if(!mark[i])
{
prime[++pn]=i; //找到一个素数,加入素数表
for(j=i*i;j<maxn;j+=i) mark[j]=1; //筛去含此素数的合数
}
}
}
void dfs(int x, int l, int r) //深搜枚举分组情况
{
if(x>n) //分组完毕
{
if(l>r) return; //避免无谓的重复处理
if(r-l<best) {best=r-l; bestl=l; bestr=r;} //找到一个更优的方案并更新
return;
}
dfs(x+1,l*p[x],r);
dfs(x+1,l,r*p[x]);
}
int main()
{
int a,b,i;
makeprime(); //预处理素数表
while(scanf("%d%d",&a,&b),a)
{
b/=a; //最小公倍数除以最大公约数
best=b+1; //置最优值为不会达到的上界
n=0; //不同的素因子的个数
for(i=1;i<=pn&&prime[i]*prime[i]<=b;i++) //分解质因数
{
if(b%prime[i]==0) {n++; p[n]=1;} //新的质因数
while(b%prime[i]==0) {p[n]*=prime[i]; b/=prime[i];}
}
if(b>1) p[++n]=b; //还有大于sqrt(b)的素因子,指数只可能为1
dfs(1,1,1); //枚举分组方案
printf("%d %d %d\n",best*a,bestl*a,bestr*a);
}
return 0;
}
求素数因子
const int N = 400010;
bool isprime[N];
int prime[34000],cnt;
int factor[30];//素数因子
void Prime() //素数打表
{
cnt=0;
memset(isprime,true,sizeof(isprime));
for(int i=2; i<N; i++){
if(isprime[i]){
for(int j=i+i; j<N; j+=i)
isprime[j]=false;
prime[cnt++]=i;
}
}
}
int pri(int a) //求出a的素数因子
{
if(isprime[a]){
factor[0]=a;
return 1;
}
int k=0,i;
for(i=0; i<cnt; i++){
if(a%prime[i]==0) factor[k++]=prime[i];
while(a%prime[i]==0) a/=prime[i];
if(a!=1&&isprime[a]){
factor[k++]=a;
return k;
}
}
return k;
}
跳跃素数问题
//从A->B能转化的要求是存在一个质数x,使得x<A ,而且A+x == B 。
//首先我们可以用线性筛素法筛出所有的素数,然后就是bfs搜索就可以了。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAXN = 1050 ;
struct Node
{
int num ,d ;
Node() {};
Node(int a, int b):num(a),d(b) {}
};
bool prim[MAXN] ;
bool mark[MAXN] ;
int C , S ,T ;
void isprim()
{
for(int i = 2 ; i < MAXN ; i ++)
prim[i] = 1 ;
for(int i = 2 ; i < MAXN ; i ++)
if(prim[i])
for(int j = 2 ; i * j < MAXN ; j ++)
prim[i * j] = 0 ;
}
int bfs(int source , int destation)
{
queue<Node> Q ;
memset(mark , 0 , sizeof(mark)) ;
Node a ;
Q.push( Node(source , 0) ) ;
mark[source] = 1 ;
while( !Q.empty() )
{
a = Q.front() ;
Q.pop() ;
//搜索比它小的素数
for(int i = 2 ; i < a.num ; i ++)
{
//判断要加的数是不是素数
if(!prim[i] )continue ;
int number = a.num + i ;
//大于所要求的目标数,直接退出
if(number > destation) break ;
if(mark[number]) continue ;
mark[number] = 1 ;
if(number == destation)
return a.d + 1;
Q.push( Node(number , a.d+1) ) ;
}
}
return -1 ;
}
int main()
{
isprim() ;
while( scanf("%d%d" , &S , &T) == 2 )
{
int d = bfs(S , T) ;
if(d==-1) printf("No path!\n") ;
else printf("Need %d step(s)\n" , d) ;
}
return 0 ;
}
线性规划数论推理
#include <stdio.h>
#include <algorithm>
using namespace std;
/*思路 : 先确定第一条边(枚举), 然后 确定 第二条边 的变化范围 ,
要注意 题目的限制条件及边的范围,同时要满足构成 三角形的条件 ,
即两边之和大于第三边。确定完第二条边 ,后确定第三条边的变化范围 ,
最后 ans=min(第二条边变化范围,第三条边变化范围).*/
int main()
{
int n,i;
int l[3], r[3];
while (scanf("%d", &n) == 1)
{
for (i= 0; i < 3; i++) scanf("%d%d", l+i, r+i);
int ans=0;
for (int i=l[0]; i<=r[0]; i++)
{
int tmp=(n-i)/2;
if (tmp<i) break;
int min1=max(l[1], i);
min1=max(min1, n/2-i +1);
int max1=min(r[1], tmp);
int min2=max(l[2], tmp+(((n-i) % 2 == 0) ? 0 : 1));
int max2=min(r[2], n-i-min1);
tmp=min(max1-min1+1, max2-min2+1);
if (tmp>0) ans += tmp;
}
printf("%d\n", ans);
}
return 0;
}
数论找规律
#include <stdio.h>
#include <string.h>
const int maxn = 1000000 + 10;
const int mod = 1000000007;
int main()
{
int d[maxn], p[maxn];
int n, a, i;
while(scanf("%d", &n) == 1)
{
memset(p, 0, sizeof(p));
d[0]=0;
for(i=1; i<=n; i++)
{
scanf("%d", &a);
if(!p[a]) d[i] = (d[i-1]*2+1) % mod;
else d[i] = (d[i-1]*2 - d[p[a]-1]) % mod;
if(d[i]<0) d[i] += mod;
p[a] = i;
}
printf("%d\n", d[n]);
}
return 0;
}
//FZU 2125 简单的等式:x^2+s(x,m)x-n=0
#include <stdio.h>
#include <math.h>
typedef long long LL;
LL s(LL x,LL m)
{
LL ans=0;
while(x)
{
ans+=x%m;
x/=m;
}
return ans;
}
LL n,m;
int T,sx;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%I64d %I64d",&n,&m);
bool flag=0;
LL x;
for(sx=1; sx<=200 && flag==0; ++sx)
{
x=(LL)(sqrt(n+sx*sx/4)-sx/2);
if(x*x+x*s(x,m)-n==0) flag=1;
}
printf("%I64d\n",flag? x:-1);
}
return 0;
}
矩阵模幂
题意:g(i)=k*i+b,sum(f(g(i)) for 0<=i<n
思路:根据Fibonacci矩阵的求法,f(i)=mat^i 令mat={1,1,1,0} ,f(g(i))=mat^g(i)=mat^(ki+b) ,
sum(f(g(i))=mat^b+mat^(k+b)+...+mat^(ki+b) for 0<=i<n,提取mat^b得:
sum(f(g(i))=mat^b+mat^b[mat^(k)+mat^2k...+mat^(ki)] ,问题转化为求 mat^(k)+mat^2k...+mat^(ki)
做法1:求mat^k,然后用O(i)的时间求sum 结果TLE
做法2:对等比矩阵进行二分求和,做法如下:
求得matk=mat^k 原式=matk+matk^2+...+matk^i
如果 i 为偶数:原式=(matk+matk^2+...+matk^i/2)+matk^(i/2)(matk+matk^2+...+matk^i/2)
如果(i)为奇数:那我们干脆先计算前面的i-1个偶数
用前面所诉的方法 tmp1=matk+matk^2+...+matk^i/2 tmp2=tmp1*matk^i/2
前偶数个的和tmp=tmp1+tmp2,最后的结果=tmp+matk^i*/
#include<stdio.h>
#include<string.h>
long long k, b,n,M ;
typedef struct Node
{
long long e[2][2] ;
} Matrix ;
Matrix init , eye ;
void Init(){
for(int i=0; i<2; i++){
for(int j=0; j<2; j++)
eye.e[i][j] = (i==j) ;
}
init.e[0][0] = init.e[0][1] = init.e[1][0] = 1 ;
init.e[1][1] = 0;
}
Matrix mul(Matrix m1, Matrix m2){
Matrix c ;
for(int i=0; i<2; i++){
for(int j=0; j<2; j++){
c.e[i][j] = 0 ;
for(int k=0; k<2; k++){
c.e[i][j] = (c.e[i][j] + m1.e[i][k]*m2.e[k][j]%M) % M ;
}
}
}
return c ;
}
Matrix pow(Matrix m1,long long kk){
Matrix res=eye ,q=m1 ;
while(kk){
if(kk & 1) res = mul(res , q) ;
kk >>= 1 ;
q = mul(q,q);
}
return res ;
}
Matrix Add(Matrix m1 ,Matrix m2){
Matrix c ;
for(int i=0; i<2; i++)
for(int j=0; j<2; j++){
c.e[i][j] = ( m1.e[i][j] + m2.e[i][j] ) % M ;
}
return c;
}
Matrix Binarysearch(Matrix m1 , long long kk){
if(kk == 0) return eye ;
if(kk == 1) return m1 ;
Matrix res ,t1,t2 ;
if(kk & 1) res = Add( Binarysearch(m1,kk-1) , pow(m1,kk));
else{
long long a = kk >> 1;
t1 = pow(m1 , a);
t2 = Binarysearch(m1, a);
res = Add(t2 ,mul(t1 , t2)) ;
}
return res ;
}
int main(){
Matrix t2 , t1 ,res ;
Init() ;
while(scanf("%lld%lld%lld%lld",&k,&b,&n,&M)==4){
t1 = pow(init , k);
t2 = pow(init , b);
res = mul(t2, Add( eye, Binarysearch(t1,n-1) ) );
printf("%lld\n",res.e[0][1]);
}
return 0 ;
}
/*题意:f(x) = K, x = 1; f(x) = (a*f(x-1) + b)%m , x > 1
Now, Your task is to calculate
( A^(f(1)) + A^(f(2)) + A^(f(3)) + ...... + A^(f(n)) ) modular P. */
#include <stdio.h>
typedef long long ll;
#define maxm 100005
//找一个k,让所有x=k*i+j,所以先求出A^k,那么A^x==(A^k)^i * A^j
ll p1[maxm+1],p2[maxm+1];
ll n, A, K, a, b, m, P,i;
void init()
{
p1[0]=p2[0]=1LL;
for(i=1; i<=maxm; i++) p1[i]=(A*p1[i-1])%P;
for(i=1; i<=maxm; i++) p2[i]=(p2[i-1]*p1[maxm])%P;
}
ll gao()
{
ll t=K,ans=0;
for(i=1; i<=n; ++i)
{
ans=(ans+p2[t/maxm]*p1[t%maxm])%P;
t=(a*t+b)%m;
}
return ans;
}
int main()
{
int cas;
scanf("%d",&cas);
for(i=1; i<=cas; ++i)
{
scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&A,&K,&a,&b,&m,&P);
init();
printf("Case #%d: %lld\n",i,gao());
}
return 0;
}
博弈论
//题意:拿一堆,数目过半,两堆每次相同
#include<stdio.h>
#include<string.h>
int b[3000],sg[3000];
int main()
{
int i,j,n,ans,date;
sg[0]=0;
for(i=1; i<2050; i++)
{//单堆石子游戏的SG函数
memset(b,0,sizeof(b));
for(j=0; j<=i/2; j++) b[sg[j]]=1;
for(j=0; b[j]==1&&j<101; j++);
sg[i]=j;
}//多堆石子游戏,石子之间没有关系,做异或运算
while(scanf("%d",&n),n)
{
ans=0;
for(i=0; i<n; i++)
{
scanf("%d",&date);
ans^=sg[date];
}
if(ans) printf("YES\n");
else printf("NO\n");
}
return 0;
}
开特兰数大数处理
1、递推公式 a[n]=a[n-1]*(4*n-2)/(n+1);
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
struct node{
int num[2000];
int g;
}a[10010];
long long int f=1000000007;
void cheng(int t,int m) //大数乘以小数
{
int k=0;
for(int i=0;i<a[t-1].g;i++)
{
a[t].num[i]=a[t-1].num[i]*m+k;
k=a[t].num[i]/10000;
a[t].num[i]%=10000;
}
a[t].g=a[t-1].g;
while(k)
{
a[t].num[a[t].g]=k%10000;
k/=10000;
a[t].g++;
}
}
void chu(int t,int m) //大数除以小数
{
long long int k=0;
for(int i=a[t].g-1;i>=0;i--)
{
k=k*10000+a[t].num[i];
a[t].num[i]=k/m;
k=k%m;
}
while(!a[t].num[a[t].g-1]) a[t].g--;
}
long long int qumo() //取模
{
long long int k=0;
for(int i=a[0].g-1;i>=0;i--)
{
k=k*10000+a[0].num[i];
a[0].num[i]=k/f;
k=k%f;
}
return k;
}
int main()
{
int i,j,l;
long long int ans;
a[1].g=1;a[1].num[0]=1;
for(i=2;i<=10000;i++)
{
cheng(i,4*i-2);
chu(i,i+1);
}
while(scanf("%d",&i)>0)
{
a[0]=a[i];
ans=qumo();
printf("%lld\n",ans);
}
return 0;
}
2、递推公式 a[n] = a[1]*a[n-1] + a[2]*a[n-2]+……+a[n-1]*a[1];
#include<cstdio>
#define maxn 10009
#define mod 1000000007
#define ll long long
using namespace std;
ll f[maxn];
ll katelant(int n)
{
if(n==2)return 1;
if(n==3)return 1;
for(int i=2; i<n; i++)
{
if(f[i]==0)f[i]=katelant(i)%mod;
if(f[n-i+1]==0)f[n-i+1]=katelant(n-i+1)%mod;
f[n]+=(f[i]*f[n-i+1])%mod;
}
return f[n];
}
int main()
{
f[2]=f[3]=1;
katelant(10005);
int n;
while(scanf("%d",&n)!=EOF)
{
printf("%lld\n",f[n+2]);
}
return 0;
}
大数模版
#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define MAXN 9999
#define MAXSIZE 10
#define DLEN 4
class BigNum
{
private:
int a[500]; //可以控制大数的位数
int len; //大数长度
public:
BigNum()
{
len = 1; //构造函数
memset(a,0,sizeof(a));
}
BigNum(const int); //将一个int类型的变量转化为大数
BigNum(const char*); //将一个字符串类型的变量转化为大数
BigNum(const BigNum &); //拷贝构造函数
BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
friend istream& operator>>(istream&, BigNum&); //重载输入运算符
friend ostream& operator<<(ostream&, BigNum&); //重载输出运算符
BigNum operator+(const BigNum &) const; //重载加法运算符,两个大数之间的相加运算
BigNum operator-(const BigNum &) const; //重载减法运算符,两个大数之间的相减运算
BigNum operator*(const BigNum &) const; //重载乘法运算符,两个大数之间的相乘运算
BigNum operator/(const int &) const; //重载除法运算符,大数对一个整数进行相除运算
BigNum operator^(const int &) const; //大数的n次方运算
int operator%(const int &) const; //大数对一个int类型的变量进行取模运算
bool operator>(const BigNum & T)const; //大数和另一个大数的大小比较
bool operator>(const int & t)const; //大数和一个int类型的变量的大小比较
void print(); //输出大数
};
BigNum::BigNum(const int b) //将一个int类型的变量转化为大数
{
int c,d = b;
len = 0;
memset(a,0,sizeof(a));
while(d > MAXN)
{
c = d - (d / (MAXN + 1)) * (MAXN + 1);
d = d / (MAXN + 1);
a[len++] = c;
}
a[len++] = d;
}
BigNum::BigNum(const char*s) //将一个字符串类型的变量转化为大数
{
int t,k,index,l,i;
memset(a,0,sizeof(a));
l=strlen(s);
len=l/DLEN;
if(l%DLEN) len++;
index=0;
for(i=l-1; i>=0; i-=DLEN)
{
t=0;
k=i-DLEN+1;
if(k<0) k=0;
for(int j=k; j<=i; j++) t=t*10+s[j]-'0';
a[index++]=t;
}
}
BigNum::BigNum(const BigNum & T) : len(T.len) //拷贝构造函数
{
int i;
memset(a,0,sizeof(a));
for(i=0; i<len; i++) a[i]=T.a[i];
}
BigNum & BigNum::operator=(const BigNum & n) //重载赋值运算符,大数之间进行赋值运算
{
int i;
len = n.len;
memset(a,0,sizeof(a));
for(i=0; i<len; i++) a[i] = n.a[i];
return *this;
}
istream& operator>>(istream & in, BigNum & b) //重载输入运算符
{
char ch[MAXSIZE*4];
int i = -1;
in>>ch;
int l=strlen(ch);
int count=0,sum=0;
for(i=l-1; i>=0;)
{
sum = 0;
int t=1;
for(int j=0; j<4&&i>=0; j++,i--,t*=10)
{
sum+=(ch[i]-'0')*t;
}
b.a[count]=sum;
count++;
}
b.len =count++;
return in;
}
ostream& operator<<(ostream& out, BigNum& b) //重载输出运算符
{
int i;
cout<<b.a[b.len - 1];
for(i=b.len-2; i>=0; i--)
{
cout.width(DLEN);
cout.fill('0');
cout<<b.a[i];
}
return out;
}
BigNum BigNum::operator+(const BigNum & T) const //两个大数之间的相加运算
{
BigNum t(*this);
int i,big; //位数
big=T.len>len ? T.len:len;
for(i=0; i<big; i++)
{
t.a[i]+=T.a[i];
if(t.a[i]>MAXN)
{
t.a[i+1]++;
t.a[i]-=MAXN+1;
}
}
if(t.a[big]!=0) t.len=big+1;
else t.len=big;
return t;
}
BigNum BigNum::operator-(const BigNum & T) const //两个大数之间的相减运算
{
int i,j,big;
bool flag;
BigNum t1,t2;
if(*this>T)
{
t1=*this;
t2=T;
flag=0;
}
else
{
t1=T;
t2=*this;
flag=1;
}
big=t1.len;
for(i=0; i<big; i++)
{
if(t1.a[i]<t2.a[i])
{
j=i+1;
while(t1.a[j]==0) j++;
t1.a[j--]--;
while(j>i) t1.a[j--]+=MAXN;
t1.a[i] += MAXN+1-t2.a[i];
}
else t1.a[i] -= t2.a[i];
}
t1.len=big;
while(t1.a[len - 1]==0 && t1.len>1)
{
t1.len--;
big--;
}
if(flag) t1.a[big-1]=0-t1.a[big-1];
return t1;
}
BigNum BigNum::operator*(const BigNum & T) const //两个大数之间的相乘运算
{
BigNum ret;
int i,j,up;
int temp,temp1;
for(i = 0 ; i < len ; i++)
{
up = 0;
for(j = 0 ; j < T.len ; j++)
{
temp = a[i] * T.a[j] + ret.a[i + j] + up;
if(temp > MAXN)
{
temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
up = temp / (MAXN + 1);
ret.a[i + j] = temp1;
}
else
{
up = 0;
ret.a[i + j] = temp;
}
}
if(up != 0)
ret.a[i + j] = up;
}
ret.len = i + j;
while(ret.a[ret.len - 1]==0 && ret.len>1) ret.len--;
return ret;
}
BigNum BigNum::operator/(const int & b) const //大数对一个整数进行相除运算
{
BigNum ret;
int i,down=0;
for(i=len-1; i>=0; i--)
{
ret.a[i]=(a[i]+down*(MAXN+1))/b;
down=a[i]+down*(MAXN+1)-ret.a[i]*b;
}
ret.len=len;
while(ret.a[ret.len-1]==0 && ret.len>1) ret.len--;
return ret;
}
int BigNum::operator %(const int & b) const //大数对一个int类型的变量进行取模运算
{
int i,d=0;
for (i = len-1; i>=0; i--)
{
d = ((d*(MAXN+1))%b + a[i])%b;
}
return d;
}
BigNum BigNum::operator^(const int & n) const //大数的n次方运算
{
BigNum t,ret(1);
int i;
if(n<0) exit(-1);
if(n==0) return 1;
if(n==1) return *this;
int m=n;
while(m>1)
{
t=*this;
for( i=1; i<<1<=m; i<<=1)
{
t=t*t;
}
m-=i;
ret=ret*t;
if(m==1) ret=ret*(*this);
}
return ret;
}
bool BigNum::operator>(const BigNum & T) const //大数和另一个大数的大小比较
{
int ln;
if(len > T.len) return true;
else if(len==T.len)
{
ln=len-1;
while(a[ln] == T.a[ln] && ln >= 0) ln--;
if(ln>=0 && a[ln]>T.a[ln]) return true;
else return false;
}
else return false;
}
bool BigNum::operator >(const int & t) const //大数和一个int类型的变量的大小比较
{
BigNum b(t);
return *this>b;
}
void BigNum::print() //输出大数
{
int i;
cout<<a[len-1];
for(i=len-2; i>=0; i--)
{
cout.width(DLEN);
cout.fill('0');
cout<<a[i];
}
cout<<endl;
}
int main()
{
int i,n;
BigNum x[101]; //定义大数的对象数组
x[0]=1;
for(i=1; i<101; i++) x[i]=x[i-1]*(4*i-2)/(i+1);
while(~scanf("%d",&n)==1) x[n].print();
return 0;
}
大数加乘
#include<cstdio>
#define MAXL 250 //高精度数组最大长度
using namespace std;
struct big_num //定义高精度数
{
int a[MAXL]; //存储高精度数的数组,每个int记录四位数字
void init() //初始化数组
{
int i;
for(i=0;i<MAXL;i++) a[i]=0; //初始各位置为0
}
big_num operator =(int obj) //重载=号,赋值符号
{
init();
int i=MAXL-1; //从MAXL-1开始存储
while(obj!=0)
{
a[i]=obj%10000; //每一个int记录四位数字
obj/=10000;
i--;
}
return *this;
}
big_num operator *(int obj) //重载乘法符号,乘以单精度数
{
int i;
big_num other;
other.init(); //调用初始化函数
int c=0; //记录进位
int k; //记录除以10000的余数
for(i=MAXL-1;i>=0;i--) //由低位开始相乘
{
k=(a[i]*obj+c)%10000; //当前四个数字
c=(a[i]*obj+c)/10000; //进位
other.a[i]=k;
}
return other;
}
big_num operator +(big_num obj) //重载加法符号,加上一个高精度数
{
big_num other;
other.init(); //调用初始化函数
int i;
int c=0; //记录进位
int k; //记录除以10000的余数
for(i=MAXL-1;i>=0;i--)
{
k=(a[i]+obj.a[i]+c)%10000; //当前四个数字
c=(a[i]+obj.a[i]+c)/10000; //进位
other.a[i]=k;
}
return other;
}
void output() //输出高精度数
{
int i,j,k;
for(i=0;i<MAXL;i++) if(a[i]!=0) break;
if(i==MAXL) {printf("0\n"); return;} //高精度数为0
printf("%d",a[i]); i++; //输出最高位
int l;
while(i<MAXL) //下面4位4位的处理
{
k=1000; //开始时最高位=a[i]/1000
l=a[i];
for(j=0;j<4;j++) //输出4位数字
{
printf("%d",l/k);
l=l%k;
k/=10; //转下一位
}
i++; //下一个四位
}
}
};
int main()
{
int t,m;
scanf("%d",&t);
while(t--)
{
k=1;
scanf("%d",&m);
while(m>4)
{
m-=3; k=k*3;
}
k=k*m;
k.output(); printf("\n");
}
return 0;
}
数据结构
并查集
void init()
{
for(int i=1; i<=n; i++) p[i]=i;
}
int find(int x)
{
if(p[x]==x) return x;
else p[x]=find1(p[x]);
}
void union(int a,int b)
{
int s=find1(a);
int t=find1(b);
if(s==t) return;
p[s]=t;
}
单调队列
#include <stdio.h>
#define MAXN 1001
int ar[MAXN];
int Qu[MAXN];
int qtail,qhead;
void insert(int p)
{
while(qtail>qhead && ar[Qu[qtail-1]] >ar[p]) qtail--;
Qu[qtail++]=p;
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m)!=EOF)
{
int i;
qtail=qhead=0;
for(i=1;i<=n;i++) scanf("%d",&ar[i]);
for(i=1;i<=m;i++) insert(i);
for( ; i<=n;i++)
{
printf("%d ",ar[ Qu[qhead] ]);
insert(i);
if(i==Qu[qhead]+m) qhead++;
}
puts("");
}
return 0;
}
字典树
/*1 求出字母的前缀的个数和最小
3 //最短的名字
aaaaa
bbb
abababab
5*/
#include <cstdio>
#include <cstring>
struct node
{
node *a[27];
int p;
}*t,*h;
char s[1005][1005];
int n,i,j,l,t0;
void make(int k,node *h)
{
h->p++;
if(k>l) return;
if(h->a[s[i][k]-'a']!=NULL) make(k+1,h->a[s[i][k]-'a']);
else
{
int x,j;
for(x=k; x<=l; x++)
{
t=new node;
t->p=1;
for(j=0; j<26; j++) t->a[j]=NULL;
h->a[s[i][x]-'a']=t;
h=t;
}
}
return;
}
void find(int k,int m,node *h)
{
t0++;
if(k==strlen(s[m])-1 || h->p==1) return ;
find(k+1,m,h->a[s[m][k+1]-'a']);
}
void release(struct node * now)
{
if(now==NULL) return;
for(int i=0; i<26; i++)
{
if(now->a[i])
{
release(now->a[i]);
now->a[i] = NULL;
}
}
free(now);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
h=new node;
h->p=0;
for(i=0; i<26; i++) h->a[i]=NULL;
for(i=1; i<=n; i++)
{
scanf("%s",s[i]);
l=strlen(s[i])-1;
make(0,h);
}
int sum=0;
for(i=1; i<=n; i++)
{
t0=0;
find(0,i,h->a[s[i][0]-'a']);
sum+=t0;
}
release(h);
printf("%d\n",sum);
}
return 0;
}
平衡二叉树(SBT算法)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 1000005
using namespace std;
struct SBT{
//左子树指针,右子树指针,大小,键值
int left,right,size,key;
void Init(){
left=right=key=0;
size=1;
}
}T[N];
int root,tot; //根的位置以及节点个数
//左旋转处理
void Left_rot(int &x){
int k=T[x].right;
T[x].right=T[k].left;
T[k].left=x;
T[k].size=T[x].size;
T[x].size=T[T[x].left].size+T[T[x].right].size+1;
x=k;
}
//右旋转处理
void Right_rot(int &x){
int k=T[x].left;
T[x].left=T[k].right;
T[k].right=x;
T[k].size=T[x].size;
T[x].size=T[T[x].left].size+T[T[x].right].size+1;
x=k;
}
//调整处理
void Maintain(int &r,bool flag){
if(flag){ //更新右子树
if(T[T[T[r].right].right].size>T[T[r].left].size)
Left_rot(r);
else if(T[T[T[r].right].left].size>T[T[r].left].size){
Right_rot(T[r].right);
Left_rot(r);
}
else
return;
}
else{ //更新在左子树
if(T[T[T[r].left].left].size>T[T[r].right].size)
Right_rot(r);
else if(T[T[T[r].left].right].size>T[T[r].right].size){
Left_rot(T[r].left);
Right_rot(r);
}
else
return;
}
//更新子树,然后再更新根,直到平衡为止
Maintain(T[r].left,false);
Maintain(T[r].right,true);
Maintain(r,false);
Maintain(r,true);
}
//插入新节点
void Insert(int &r,int k){
if(r==0){
r=++tot;
T[r].Init();
T[r].key=k;
}
else{
T[r].size++;
if(k<T[r].key)
Insert(T[r].left,k);
else
Insert(T[r].right,k);
//插入后要调整,保证平衡
Maintain(r,k>=T[r].key);
}
}
//删除结点,利用的是前驱替换
int Remove(int &r,int k){
int d_key;
if(!r)
return 0;
T[r].size--;
//前者说明就是要删的节点,后两者说明不存在此节点
if(T[r].key==k||(T[r].left==0&&k<T[r].key)||(T[r].right==0&&k>T[r].key)){
d_key=T[r].key;
if(T[r].left&&T[r].right)
T[r].key=Remove(T[r].left,k+1);
else
r=T[r].left+T[r].right;
}
else Remove(k<T[r].key?T[r].left:T[r].right,k);
}
//取得最大值,即一直遍历到最右的结点
int Get_Max(int r){
while(T[r].right)
r=T[r].right;
return r;
}
//取得最小值,即一直遍历到最左的结点
int Get_Min(int r){
while(T[r].left)
r=T[r].left;
return r;
}
//获得前驱
int Get_Pre(int &r,int y,int k){
if(r==0) return y;
if(k>T[r].key)
Get_Pre(T[r].right,r,k);
else
Get_Pre(T[r].left,y,k);
}
//获得后继
int Get_Next(int &r,int y,int k){
if(r==0) return y;
if(k<T[r].key)
Get_Next(T[r].left,r,k);
else
Get_Next(T[r].right,y,k);
}
//取得第K小的数,注:暂不能解决有重复数的
int Get_Kth(int &r,int k){
int t=T[T[r].left].size+1;
if(t==k) return T[r].key;
if(t<k) return Get_Kth(T[r].right,k-r);
else return Get_Kth(T[r].left,k);
}
//获得结点的名次
int Get_Rank(int &r,int k){
if(k<T[r].key)
return Get_Rank(T[r].left,k);
else if(k>T[r].key)
return Get_Rank(T[r].right,k)+T[T[r].left].size+1;
else
return T[T[r].left].size+1;
}
//排序
void Inorder(int &r){
if(r==0) return;
Inorder(T[r].left);
printf("%d\n",T[r].key);
Inorder(T[r].right);
}
排序二叉树
1 BST树的建立与维护
已知节点的左右孩子,和节点的权值,求出维护一棵平衡二叉树所执行的操作
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
int v[5100],l[5100],r[5100],num;
int flag[5100],a[5100],dp[5100];
void find_root(int x)//寻找树的根节点
{
if(flag[x]==1 || x==0) return;
flag[x]=1;
find_root(l[x]);
find_root(r[x]);
}
void dfs(int x)//中序遍历整棵树,把所有节点的权值存入a数组中
{
if(x==0) return;
dfs(l[x]);
a[num++]=v[x];
dfs(r[x]);
}
int main()
{
int T,n,i,j;
int root;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1; i<=n; i++) scanf("%d %d %d",&v[i],&l[i],&r[i]);
memset(flag,0,sizeof(flag));
for(i=1; i<=n; i++)
if(!flag[i])
{
root=i;
find_root(i);
}
num=0;
dfs(root);
for(i=0; i<=n; i++) dp[i]=1;
//求出数组a中最长递增子序列长度存入dp数组中
for(i=1; i<n; i++)
for(j=i-1; j>=0; j--)
if(dp[i]<dp[j]+1 && a[i]>a[j]) dp[i]=dp[j]+1;
printf("%d\n",n-*max_element(dp,dp+n));
}
return 0;
}
2 Defense Lines
删除连续子序列,求最长连续子序列的长度
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX=200005,INF=1<<30;
const int ROOT=0;
int n;
int a[MAX],c[MAX];
int dpL[MAX],dpR[MAX];
int ans,len;
void setDp(){
dpR[n-1]=1;
for(int i=n-2;i>=0;i--){
if(a[i]<a[i+1])
dpR[i]=dpR[i+1]+1;
else
dpR[i]=1;
}
dpL[0]=1;
for(int i=1;i<n;i++){
if(a[i]>a[i-1]){
dpL[i]=dpL[i-1]+1;
}
else
dpL[i]=1;
}
return ;
}
int bSearch(int x,int y,const int &val){
int mid;
while(x<y){
mid=(x+y)>>1;
if(c[mid]<val)
x=mid+1;
else
y=mid;
}
return x-1;
}
int main()
{
int T,pos;
scanf("%d",&T);
while(T--){
memset(c,0,sizeof(c));
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
setDp();
ans=1,len=1;
c[len]=a[0];
for(int i=1;i<n;i++){
pos=bSearch(1,len+1,a[i]);
if(ans<pos+dpR[i])
ans=pos+dpR[i];
if(a[i]<c[dpL[i]] || dpL[i]>len){
c[dpL[i]]=a[i];
len=max(len,dpL[i]);
}
}
printf("%d\n",ans);
}
return 0;
}
堆
1大顶堆
#include <stdio.h>
#include <algorithm>
using namespace std;
#define MAX 10001
int n, heap[MAX]; //n是堆的规模,heap 是堆
void down(int idx) //下调
{
int sub = (idx<<1)+1;
while(sub<n)
{
if(sub+1<n&&heap[sub]<=heap[sub+1])sub++;
if(heap[sub]<=heap[idx])break;
swap(heap[sub],heap[idx]);
sub = ( (idx = sub)<<1 )+1;
}
}
void up(int idx) //上调
{
int fa = (idx-1)>>1;
while(idx)
{
if(heap[fa]>=heap[idx])break;
swap(heap[fa],heap[idx]);
fa=((idx=fa)-1)>>1;
}
}
int pop() //必须有元素
{
swap(heap[--n],heap[0]);
down(0);
return heap[n];
}
void build()
{
for(int now=(n>>1)-1; now+1; now--)down(now); //O(n)建堆
}
2二分堆(binary)
//可插入,获取并删除最小(最大)元素,复杂度均O(logn)
//可更改元素类型,修改比较符号或换成比较函数
#define MAXN 10000
#define _cp(a,b) ((a)<(b))
typedef int elem_t;
struct heap{
elem_t h[MAXN];
int n,p,c;
void init(){n=0;}
void ins(elem_t e){
for (p=++n;p>1&&_cp(e,h[p>>1]);h[p]=h[p>>1],p>>=1);
h[p]=e;
}
int del(elem_t& e){
if (!n) return 0;
for (e=h[p=1],c=2;c<n&&_cp(h[c+=(c<n-1&&_cp(h[c+1],h[c]))],h[n]);h[p]=h[c],p=c,c<<=1);
h[p]=h[n--];return 1;
}
};
3映射二分堆(mapped)
//可插入,获取并删除任意元素,复杂度均O(logn)
//插入时提供一个索引值,删除时按该索引删除,获取并删除最小元素时一起获得该索引
//索引值范围0..MAXN-1,不能重复,不负责维护索引的唯一性,不在此返回请另外映射
//主要用于图论算法,该索引值可以是节点的下标
//可更改元素类型,修改比较符号或换成比较函数
#define MAXN 10000
#define _cp(a,b) ((a)<(b))
typedef int elem_t;
struct heap{
elem_t h[MAXN];
int ind[MAXN],map[MAXN],n,p,c;
void init(){n=0;}
void ins(int i,elem_t e){
for (p=++n;p>1&&_cp(e,h[p>>1]);h[map[ind[p]=ind[p>>1]]=p]=h[p>>1],p>>=1);
h[map[ind[p]=i]=p]=e;
}
int del(int i,elem_t& e){
i=map[i];if (i<1||i>n) return 0;
for (e=h[p=i];p>1;h[map[ind[p]=ind[p>>1]]=p]=h[p>>1],p>>=1);
for (c=2;c<n&&_cp(h[c+=(c<n-1&&_cp(h[c+1],h[c]))],h[n]);h[map[ind[p]=ind[c]]=p]=h[c],p=c,c<<=1);
h[map[ind[p]=ind[n]]=p]=h[n];n--;return 1;
}
int delmin(int& i,elem_t& e){
if (n<1) return 0;i=ind[1];
for (e=h[p=1],c=2;c<n&&_cp(h[c+=(c<n-1&&_cp(h[c+1],h[c]))],h[n]);h[map[ind[p]=ind[c]]=p]=h[c],p=c,c<<=1);
h[map[ind[p]=ind[n]]=p]=h[n];n--;return 1;
}
};
树状数组
1子段和
int lowbit(int x)//计算2^x
{
return x & (-x);
}
int getsum(int id)//求1到id的和
{
int sum=0;
while(id > 0) {
sum += c[id];
id -= lowbit(id);
}
return sum;
}
void plus(int id, int num) {
while(id<=n) {
c[id] += num;
id += lowbit(id);
}
}
2子阵和
//求sum{a[0..m-1][0..n-1]}
//维护和查询复杂度均为O(logm*logn)
//用于动态求子阵和,数组内容保存在sum.a[][]中
//可以改成其他数据类型
#include <string.h>
#define lowbit(x) ((x)&((x)^((x)-1)))
#define MAXN 100
typedef int elem_t;
struct sum{
elem_t a[MAXN][MAXN],c[MAXN][MAXN],ret;
int m,n,t;
void init(int i,int j){memset(a,0,sizeof(a));memset(c,0,sizeof(c));m=i,n=j;}
void update(int i,int j,elem_t v){
for (v-=a[i][j],a[i++][j++]+=v,t=j;i<=m;i+=lowbit(i))
for (j=t;j<=n;c[i-1][j-1]+=v,j+=lowbit(j));
}
elem_t query(int i,int j){
for (ret=0,t=j;i;i^=lowbit(i))
for (j=t;j;ret+=c[i-1][j-1],j^=lowbit(j));
return ret;
}
};
线段树
1 RMQ问题(频繁出现的数值)
给出一个非降序排列的整数数组a1,a2……,an,对于一系列查询,输出序列中次数最多的值出现的次数。
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 100000 + 5;
const int maxlog = 20;
// 区间最*大*值
struct RMQ {
int d[maxn][maxlog];
void init(const vector<int>& A) {
int n = A.size();
for(int i = 0; i < n; i++) d[i][0] = A[i];
for(int j = 1; (1<<j) <= n; j++)
for(int i = 0; i + (1<<j) - 1 < n; i++)
d[i][j] = max(d[i][j-1], d[i + (1<<(j-1))][j-1]);
}
int query(int L, int R) {
int k = 0;
while((1<<(k+1)) <= R-L+1) k++; // 如果2^(k+1)<=R-L+1,那么k还可以加1
return max(d[L][k], d[R-(1<<k)+1][k]);
}
};
int a[maxn], num[maxn], left[maxn], right[maxn];
RMQ rmq;
int main() {
int n, q;
while(scanf("%d%d", &n, &q) == 2) {
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
a[n] = a[n-1] + 1; // 哨兵
int start = -1;
vector<int> count;
for(int i = 0; i <= n; i++) {
if(i == 0 || a[i] > a[i-1]) { // 新段开始
if(i > 0) {
count.push_back(i - start);
for(int j = start; j < i; j++) {
num[j] = count.size() - 1; left[j] = start; right[j] = i-1;
}
}
start = i;
}
}
rmq.init(count);
while(q--) {
int L, R, ans;
scanf("%d%d", &L, &R); L--; R--;
if(num[L] == num[R]) ans = R-L+1;
else {
ans = max(R-left[R]+1, right[L]-L+1);
if(num[L]+1 < num[R]) ans = max(ans, rmq.query(num[L]+1, num[R]-1));
}
printf("%d\n", ans);
}
}
return 0;
}
2点维护(在给定区间内动态维护最大连续和)
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 1000010
#define lson p<<1
#define rson p<<1|1
using namespace std;
struct Node
{
int l,r;
int num; //标记区间数字是否相同
} node[N<<2];
void building(int l,int r,int p)
{
node[p].l = l;
node[p].r = r;
node[p].num = -1;
if(l==r)
{
scanf("%d",&node[p].num);
return;
}
int mid = (l+r)>>1;
building(l,mid,lson);
building(mid+1,r,rson);
if(node[lson].num!=-1&&node[lson].num==node[rson].num) //向上更新
{
node[p].num = node[lson].num;
}
}
int opreate(int op,int opn,int num) //操作函数 op:操作符 opn,num:操作数
{
if(op==1) return opn#
if(op==2) return opn|num;
if(op==3) return opn^num;
}
void update(int l,int r,int p,int opn,int op)
{
if(node[p].l==l&&node[p].r==r&&node[p].num>=0) //当找到区间并且区间被相同的数覆盖
{
node[p].num = opreate(op,opn,node[p].num);
return;
}
if(node[p].num>=0) //经过该区间时,向下更新
{
node[lson].num = node[rson].num = node[p].num;
node[p].num = -1;
}
int mid = (node[p].l+node[p].r)>>1;
if(r<=mid) update(l,r,lson,opn,op);
else if(l>mid) update(l,r,rson,opn,op);
else
{
update(l,mid,lson,opn,op);
update(mid+1,r,rson,opn,op);
}
if(node[lson].num!=-1&&node[lson].num==node[rson].num) //向上更新
{
node[p].num = node[lson].num;
}
}
__int64 query(int l,int r,int p)
{
if(node[p].l==l&&node[p].r==r&&node[p].num>=0)
{
return node[p].num*(node[p].r-node[p].l+1);
}
if(node[p].num>=0) //经过该区间时,向下更新
{
node[lson].num = node[rson].num = node[p].num;
node[p].num = -1;
}
int mid = (node[p].l+node[p].r)>>1;
if(r<=mid) return query(l,r,lson);
else if(l>mid) return query(l,r,rson);
else return query(l,mid,lson)+query(mid+1,r,rson);
if(node[lson].num!=-1&&node[lson].num==node[rson].num) //向上更新
{
node[p].num = node[lson].num;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
char op[5];
building(1,n,1);
while(m--)
{
scanf("%s",op);
int opn,a,b;
if(op[0]=='S')
{
scanf("%d%d",&a,&b);
printf("%I64d\n",query(a+1,b+1,1));
}
else
{
scanf("%d%d%d",&opn,&a,&b);
if(op[0]=='A') update(a+1,b+1,1,opn,1);
else if(op[0]=='O') update(a+1,b+1,1,opn,2);
else if(op[0]=='X') update(a+1,b+1,1,opn,3);
}
}
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 500000 + 10;
const int maxnode = 1000000 + 10;
typedef long long LL;
typedef pair<int,int> Interval;
//用来表示一个二元组或元素对,提供了按照字典序对元素对进行大小比较运算符模板函数
LL prefix_sum[maxn];
LL sum(int L, int R) {
return prefix_sum[R] - prefix_sum[L-1];
}
LL sum(Interval p) {
return sum(p.first, p.second);
}
Interval better(Interval a, Interval b) {
if(sum(a) != sum(b)) return sum(a) > sum(b) ? a : b;
return a < b ? a : b; // 利用pair自带的字典序
}
int qL, qR;
struct IntervalTree {
int max_prefix[maxnode];
int max_suffix[maxnode];
Interval max_sub[maxnode];
void build(int o, int L, int R) {
if(L == R) {
max_prefix[o] = max_suffix[o] = L;
max_sub[o] = make_pair(L, L);
} else {
int M = L + (R-L)/2;
// 递归创建子树
int lc = o*2, rc = o*2+1;
build(lc, L, M);
build(rc, M+1, R);
// 递推max_prefix
LL v1 = sum(L, max_prefix[lc]);
LL v2 = sum(L, max_prefix[rc]);
if(v1 == v2) max_prefix[o] = min(max_prefix[lc], max_prefix[rc]);
else max_prefix[o] = v1 > v2 ? max_prefix[lc] : max_prefix[rc];
// 递推max_suffix
v1 = sum(max_suffix[lc], R);
v2 = sum(max_suffix[rc], R);
if(v1 == v2) max_suffix[o] = min(max_suffix[lc], max_suffix[rc]);
else max_suffix[o] = v1 > v2 ? max_suffix[lc] : max_suffix[rc];
// 递推max_sub
max_sub[o] = better(max_sub[lc], max_sub[rc]); // 完全在左子树或者右子树
max_sub[o] = better(max_sub[o], make_pair(max_suffix[lc], max_prefix[rc])); //跨越中线
}
}
Interval query_prefix(int o, int L, int R) {
if(max_prefix[o] <= qR) return make_pair(L, max_prefix[o]);
int M = L + (R-L)/2;
int lc = o*2, rc = o*2+1;
if(qR <= M) return query_prefix(lc, L, M);
Interval i = query_prefix(rc, M+1, R);
i.first = L;
return better(i, make_pair(L, max_prefix[lc]));
}
Interval query_suffix(int o, int L, int R) {
if(max_suffix[o] >= qL) return make_pair(max_suffix[o], R);
int M = L + (R-L)/2;
int lc = o*2, rc = o*2+1;
if(qL > M) return query_suffix(rc, M+1, R);
Interval i = query_suffix(lc, L, M);
i.second = R;
return better(i, make_pair(max_suffix[rc], R));
}
Interval query(int o, int L, int R) {
if(qL <= L && R <= qR) return max_sub[o];
int M = L + (R-L)/2;
int lc = o*2, rc = o*2+1;
if(qR <= M) return query(lc, L, M);
if(qL > M) return query(rc, M+1, R);
Interval i1 = query_prefix(rc, M+1, R); // 右半的前缀
Interval i2 = query_suffix(lc, L, M); // 左半的后缀
Interval i3 = better(query(lc, L, M), query(rc, M+1, R));
return better(make_pair(i2.first, i1.second), i3);
}
};
IntervalTree tree;
int main() {
int kase = 0, n, a, Q;
while(scanf("%d%d", &n, &Q) == 2) {
prefix_sum[0] = 0;
for(int i = 0; i < n; i++) {
scanf("%d", &a);
prefix_sum[i+1] = prefix_sum[i] + a;
}
tree.build(1, 1, n);
printf("Case %d:\n", ++kase);
while(Q--) {
int L, R;
scanf("%d%d", &L, &R);
qL = L; qR = R;
Interval ans = tree.query(1, 1, n);
printf("%d %d\n", ans.first, ans.second);
}
}
return 0;
}
3区间维护
a 区间维护连续递增子序列最长个数
#define maxn 100005
#include<algorithm>
#include<cstdio>
using namespace std;
struct Node
{
int lm,mm,rm;//以左端点开始的LCIS,整个区间的LCIS,以右端点结尾的LCIS
int left,right;//区间端点
int len()
{
return right-left+1;
}
} tree[maxn<<2];
int N,M,val[maxn];
void maintain(int id)//根据左右孩子信息来更新自己
{
int mid=(tree[id].left+tree[id].right)>>1;
if(val[mid]<val[mid+1])//左右子树存在合并的个数
{
tree[id].lm=tree[id<<1].lm+(tree[id<<1].lm==tree[id<<1].len()?tree[id<<1|1].lm:0);
tree[id].rm=tree[id<<1|1].rm+(tree[id<<1|1].rm==tree[id<<1|1].len()?tree[id<<1].rm:0);
tree[id].mm=max(max(tree[id].lm,tree[id].rm),max(tree[id<<1].mm,tree[id<<1|1].mm));
tree[id].mm=max(tree[id].mm,tree[id<<1].rm+tree[id<<1|1].lm);
}
else
{
tree[id].lm=tree[id<<1].lm;
tree[id].rm=tree[id<<1|1].rm;
tree[id].mm=max(tree[id<<1].mm,tree[id<<1|1].mm);
}
}
void build(int id,int left,int right)
{
tree[id].left=left,tree[id].right=right;
if(left==right)
{
tree[id].lm=tree[id].mm=tree[id].rm=1;
return ;
}
if(right-left>0)
{
int mid=(left+right)>>1;
build(id<<1,left,mid);
build(id<<1|1,mid+1,right);
}
maintain(id);//根据左右孩子更新自己的区间信息LCIS
}
void update(int id,int left,int right,int va)//将区间[left,right]的值更新为va
{
if(tree[id].left==tree[id].right)
{
val[left]=va;
return;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(right<=mid) update(id<<1,left,right,va);
else update(id<<1|1,left,right,va);
maintain(id);
}
int query(int id,int left,int right)//查找区间[left,right]内的LCIS
{
if(left==tree[id].left&&right==tree[id].right) return tree[id].mm;
int mid=(tree[id].left+tree[id].right)>>1;
if(right<=mid) return query(id<<1,left,right);
else if(left>mid) return query(id<<1|1,left,right);
else
{
int mm=max(query(id<<1,left,mid),query(id<<1|1,mid+1,right));
if(val[mid]<val[mid+1])
//tree[id<<1].rm是tree[id<<1]所代表区间的rm,可能会大于[left,mid]区间的总长度,同理另一边
mm=max(mm,min(mid-left+1,tree[id<<1].rm)+min(right-mid,tree[id<<1|1].lm));
return mm;
}
}
int main()
{
//freopen("in.txt","r",stdin);
int cases,a,b,i;
char op;
scanf("%d",&cases);
while(cases--)
{
scanf("%d%d",&N,&M);
for(i=1; i<=N; i++) scanf("%d",&val[i]);
build(1,0,N);
for(i=0; i<M; i++)
{
scanf("%s%d%d",&op,&a,&b);
if(op=='Q') a++,b++,printf("%d\n",query(1,a,b));
else a++,update(1,a,a,b);
}
}
return 0;
}
b 快速子矩阵操作
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxnode = 1<<17;
int _sum, _min, _max, op, x1, x2, y1, y2, x, v;
/*1 x1 y1 x2 y2 v 子矩阵(x1,y1,x2,y2)的所有元素增加v(v>0)
2 x1 y1 x2 y2 v 子矩阵(x1,y1,x2,y2)的所有元素设为v(v>0)
3 x1 y1 x2 y2 */ 查询子矩阵(x1,y1,x2,y2)的元素和、最小值和最大值
struct IntervalTree {
int sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode];
// 维护信息
void maintain(int o, int L, int R) {
int lc = o*2, rc = o*2+1;
if(R > L) {
sumv[o] = sumv[lc] + sumv[rc];
minv[o] = min(minv[lc], minv[rc]);
maxv[o] = max(maxv[lc], maxv[rc]);
}
if(setv[o] >= 0) { minv[o] = maxv[o] = setv[o]; sumv[o] = setv[o] * (R-L+1); }
if(addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (R-L+1); }
}
// 标记传递
void unmark(int o) {
int lc = o*2, rc = o*2+1;
if(setv[o] >= 0) {
setv[lc] = setv[rc] = setv[o];
addv[lc] = addv[rc] = 0;
setv[o] = -1; // 清除本结点标记
}
if(addv[o]) {
addv[lc] += addv[o];
addv[rc] += addv[o];
addv[o] = 0; // 清除本结点标记
}
}
void update(int o, int L, int R) {
int lc = o*2, rc = o*2+1;
if(y1 <= L && y2 >= R) { // 标记修改
if(op == 1) addv[o] += v;
else { setv[o] = v; addv[o] = 0; }
} else {
unmark(o);
int M = L + (R-L)/2;
if(y1 <= M) update(lc, L, M); else maintain(lc, L, M);
if(y2 > M) update(rc, M+1, R); else maintain(rc, M+1, R);
}
maintain(o, L, R);
}
void query(int o, int L, int R, int add) {
if(setv[o] >= 0) {
int v = setv[o] + add + addv[o];
_sum += v * (min(R,y2)-max(L,y1)+1);
_min = min(_min, v);
_max = max(_max, v);
} else if(y1 <= L && y2 >= R) {
_sum += sumv[o] + add * (R-L+1);
_min = min(_min, minv[o] + add);
_max = max(_max, maxv[o] + add);
} else {
int M = L + (R-L)/2;
if(y1 <= M) query(o*2, L, M, add + addv[o]);
if(y2 > M) query(o*2+1, M+1, R, add + addv[o]);
}
}
};
const int maxr = 20 + 5;
const int INF = 1000000000;
IntervalTree tree[maxr];
int main() {
int r, c, m;
while(scanf("%d%d%d", &r, &c, &m) == 3) {
memset(tree, 0, sizeof(tree));
for(x = 1; x <= r; x++) {
memset(tree[x].setv, -1, sizeof(tree[x].setv));
tree[x].setv[1] = 0;
}
while(m--) {
scanf("%d%d%d%d%d", &op, &x1, &y1, &x2, &y2);
if(op < 3) {
scanf("%d", &v);
for(x = x1; x <= x2; x++) tree[x].update(1, 1, c);
} else {
_sum = 0; _min = INF; _max = -INF;
for(x = x1; x <= x2; x++) tree[x].query(1, 1, c, 0);
printf("%d %d %d\n", _sum, _min, _max);
}
}
}
return 0;
}
4线段树求最小值
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=131072;
const int INF=1000000000;
struct Node {
int min,max,min_diff;
}node[N<<1];
int query() {
return node[1].min_diff<N?node[1].min_diff:-1;
}
void update(int p) {
int left_child=p<<1;
int right_child=(p<<1)+1;
node[p].min=min(node[left_child].min,node[right_child].min);
node[p].max=max(node[left_child].max,node[right_child].max);
node[p].min_diff=min(node[left_child].min_diff,node[right_child].min_diff);
node[p].min_diff=min(node[p].min_diff,node[right_child].min-node[left_child].max);
}
void add(int m) {
int cur=m+N-1;
if(node[cur].min==INF) {
node[cur].min=m;
node[cur].max=m;
do {
cur=cur>>1;
update(cur);
} while (cur>1);
}
}
void remove(int m) {
int cur=m+N-1;
if(node[cur].min<INF) {
node[cur].min=INF;
node[cur].max=-INF;
do {
cur=cur>>1;
update(cur);
} while (cur>1);
}
}
int main()
{
//freopen( "in.txt","r",stdin );
//freopen( "out.txt","w",stdout );
int casen;
scanf("%d",&casen);
while(casen--) {
int i,n;
for(i=1;i<(N<<1);i++) {
node[i].min=INF;
node[i].max=-INF;
node[i].min_diff=INF;
}
scanf("%d",&n);
for(i=0;i<n;i++) {
char cmd[10];
scanf("%s",cmd);
if(cmd[0]=='q') printf("%d\n",query());
else {
int m;
scanf("%d",&m);
if(cmd[0]=='g') add(m);
else remove(m);
}
}
if(casen>0) printf("\n");
}
return 0;
}
/*划分树----求区间第K小/大值
这道题是求区间求区间[l,r]中 满足 A<=xi<=B; xi属于[l,r]。求xi的个数。
如何转化的呢?
枚举区间第k小数==A。此时能求出一个值 hxl
枚举区间第K小数==B,此时得到一个值 tom
显然满足tom>=hxl。
那在这范围的数,是不是就满足了 A<=xi<=B !
(求的是某个数字是区间里面第几小。)应该说是求A的下届,B的上届值。
二分枚举,平均的时间会更小。 */
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 100010
using namespace std;
int toleft[30][N];
int Tree[30][N];
int Sort[N];
void build(int l,int r,int dep)
{
int mid=(l+r)/2,sum=mid-l+1,lpos,rpos,i;
if(l==r) return ;
for(i=l;i<=r;i++)
if(Tree[dep][i]<Sort[mid]) sum--;
lpos=l;
rpos=mid+1;
for(i=l;i<=r;i++)
{
if(Tree[dep][i]<Sort[mid]) Tree[dep+1][lpos++]=Tree[dep][i];
else if(Tree[dep][i]==Sort[mid] && sum>0)
{
Tree[dep+1][lpos++]=Tree[dep][i];
sum--;
}
else Tree[dep+1][rpos++]=Tree[dep][i];
toleft[dep][i]=toleft[dep][l-1]+lpos-l;
}
build(l,mid,dep+1);
build(mid+1,r,dep+1);
}
int query(int L,int R,int l,int r,int dep,int k)
{
int mid=(L+R)/2,newl,newr,cur;
if(l==r) return Tree[dep][l];
cur=toleft[dep][r]-toleft[dep][l-1];
if(cur>=k)
{
newl=L+toleft[dep][l-1]-toleft[dep][L-1];
newr=newl+cur-1;
return query(L,mid,newl,newr,dep+1,k);
}
else
{
newr=r+(toleft[dep][R]-toleft[dep][r]);
newl=newr-(r-l-cur);
return query(mid+1,R,newl,newr,dep+1,k-cur);
}
}
int EF1(int l,int r,int num,int n) //枚举下届
{
int low=1,right=r-l+1,mid,tmp;
mid=(low+right)/2;
while(low<right)
{
tmp=query(1,n,l,r,0,mid);
if(tmp>=num)
right=mid-1;
else low=mid; //!!!!
mid=(low+right+1)/2; //!!! 加了一个1。
}
return low;
}
int EF2(int l,int r,int num,int n)
{
int low=1,right=r-l+1,mid,tmp;
mid=(low+right)/2;
while(low<right)
{
tmp=query(1,n,l,r,0,mid);
if(tmp>num)
right=mid; //!!!
else low=mid+1;
mid=(low+right)/2; //木有加1
}
return low;
}
int main()
{
int T,n,m,i,l,r,k,a,b,hxl,tom,qq;
scanf("%d",&T);
{
for(qq=1;qq<=T;qq++)
{
scanf("%d%d",&n,&m);
memset(Tree,0,sizeof(Tree));
for(i=1;i<=n;i++)
{
scanf("%d",&Tree[0][i]);
Sort[i]=Tree[0][i];
}
sort(Sort+1,Sort+1+n);
build(1,n,0);
printf("Case #%d:\n",qq);
while(m--)
{
scanf("%d%d%d%d",&l,&r,&a,&b);
hxl=query(1,n,l,r,0,1);
if(a<=hxl) hxl=1; //对临界条件的判断。如果A就是最小的,木有下届
else
{
hxl=EF1(l,r,a,n);
hxl++;
}
tom=query(1,n,l,r,0,r-l+1);
if(b>=tom) tom=r-l+1; //如果B是该区间[l,r]最大的,显然木有上届
else
{
tom=EF2(l,r,b,n);
tom--;
}
hxl=tom-hxl+1;
printf("%d\n",hxl);
}
}
}
return 0;
}
5 带循环移动的RMQ
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100010
#define INF 0x7fffffff
using namespace std;
int a[maxn];
struct seg_tree
{
int a,b;
int min_num;
} s_tree[maxn*6];
void build(int x,int y,int k)
{
int mid=(x+y)/2,i,tmp=INF;
s_tree[k].a=x;
s_tree[k].b=y;
for(i=x; i<=y; i++) if(tmp>a[i]) tmp=a[i];
s_tree[k].min_num=tmp;
if(y-x>=1)
{
build(x,mid,k*2);
build(mid+1,y,k*2+1);
}
}
int query(int x,int y,int k)
{
if(x<=s_tree[k].a && y>=s_tree[k].b) return s_tree[k].min_num;
int mid=(s_tree[k].a+s_tree[k].b)/2,ret;
if(x<=mid)
{
if(y>=mid+1) ret=min(query(x,y,k*2),query(x,y,k*2+1));
else ret=query(x,y,k*2);
}
else ret=query(x,y,k*2+1);
return ret;
}
void update(int x,int k,int p)
{
if(x>=s_tree[k].a&&x<=s_tree[k].b)
{
if(s_tree[k].min_num>=p) s_tree[k].min_num=p;
else if(s_tree[k].min_num==a[x])
{
int i,tmp=INF;
for(i=s_tree[k].a; i<=s_tree[k].b; i++) if(tmp>a[i]&&i!=x) tmp=a[i];
s_tree[k].min_num=tmp;
if(s_tree[k].min_num>p) s_tree[k].min_num=p;
}
}
else return;
update(x,k*2,p);
update(x,k*2+1,p);
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
int i,j,k,C[40];
char cmd[40];
for(i=1; i<=n; i++) scanf("%d",&a[i]);
build(1,n,1);
for(i=1; i<=m; i++)
{
scanf("%s",&cmd);
if(cmd[0]=='f')
{
int x, y;
sscanf(cmd+5,"%d,%d",&x,&y);
printf("%d\n",query(x,y,1));
}
else
{
char *p=strtok(cmd+5,",");
int x=0,tmp;
while(p)
{
sscanf(p,"%d",&C[x]);
p=strtok(NULL,",");
x++;
}
for(j=0; j<x; j++)
{
if(j==0) update(C[x-1],1,a[C[0]]);
else update(C[j-1],1,a[C[j]]);
}
for(j=0; j<x; j++)
{
if(j==0) tmp=a[C[0]];
else a[C[j-1]]=a[C[j]];
}
a[C[x-1]]=tmp;
}
}
}
return 0;
}
计算机几何
最近点对
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define Max(x,y) (x)>(y)?(x):(y)
struct Q
{
double x, y;
}q[100001], sl[10], sr[10];
int cntl, cntr, lm, rm;
double ans;
int cmp(const void*p1, const void*p2)
{
struct Q*a1=(struct Q*)p1;
struct Q*a2=(struct Q*)p2;
if (a1->x<a2->x)return -1;
else if (a1->x==a2->x)return 0;
else return 1;
}
double CalDis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void MinDis(int l, int r)
{
if (l==r) return;
double dis;
if (l+1==r)
{
dis=CalDis(q[l].x,q[l].y,q[r].x,q[r].y);
if (ans>dis) ans=dis;
return;
}
int mid=(l+r)>>1, i, j;
MinDis(l,mid);
MinDis(mid+1,r);
lm=mid+1-5;
if (lm<l) lm=l;
rm=mid+5;
if (rm>r) rm=r;
cntl=cntr=0;
for (i=mid;i>=lm;i--)
{
if (q[mid+1].x-q[i].x>=ans)break;
sl[++cntl]=q[i];
}
for (i=mid+1;i<=rm;i++)
{
if (q[i].x-q[mid].x>=ans)break;
sr[++cntr]=q[i];
}
for (i=1;i<=cntl;i++)
for (j=1;j<=cntr;j++)
{
dis=CalDis(sl[i].x,sl[i].y,sr[j].x,sr[j].y);
if (dis<ans) ans=dis;
}
}
int main (void)
{
int n, i;
while (scanf("%d",&n)==1&&n)
{
for (i=1;i<=n;i++)
scanf("%lf %lf", &q[i].x,&q[i].y);
qsort(q+1,n,sizeof(struct Q),cmp);
ans=CalDis(q[1].x,q[1].y,q[2].x,q[2].y);
MinDis(1,n);
printf("%.2lf\n",ans/2.0);
}
return 0;
}
最小包围圆
#include<stdio.h>
#include<string.h>
#include<math.h>
struct Point
{
double x;
double y;
}pt[1005];
struct Traingle
{
struct Point p[3];
};
struct Circle
{
struct Point center;
double r;
}ans;
//计算两点距离
double Dis(struct Point p, struct Point q)
{
double dx=p.x-q.x;
double dy=p.y-q.y;
return sqrt(dx*dx+dy*dy);
}
//计算三角形面积
double Area(struct Traingle ct)
{
return fabs((ct.p[1].x-ct.p[0].x)*(ct.p[2].y-ct.p[0].y)-(ct.p[2].x-ct.p[0].x)*(ct.p[1].y-ct.p[0].y))/2.0;
}
//求三角形的外接圆,返回圆心和半径(存在结构体"圆"中)
struct Circle CircumCircle(struct Traingle t)
{
struct Circle tmp;
double a, b, c, c1, c2;
double xA, yA, xB, yB, xC, yC;
a = Dis(t.p[0], t.p[1]);
b = Dis(t.p[1], t.p[2]);
c = Dis(t.p[2], t.p[0]);
//根据S = a * b * c / R / 4;求半径R
tmp.r = (a*b*c)/(Area(t)*4.0);
xA = t.p[0].x;
yA = t.p[0].y;
xB = t.p[1].x;
yB = t.p[1].y;
xC = t.p[2].x;
yC = t.p[2].y;
c1 = (xA*xA+yA*yA - xB*xB-yB*yB) / 2;
c2 = (xA*xA+yA*yA - xC*xC-yC*yC) / 2;
tmp.center.x = (c1*(yA - yC)-c2*(yA - yB)) / ((xA - xB)*(yA - yC)-(xA - xC)*(yA - yB));
tmp.center.y = (c1*(xA - xC)-c2*(xA - xB)) / ((yA - yB)*(xA - xC)-(yA - yC)*(xA - xB));
return tmp;
}
//确定最小包围圆
struct Circle MinCircle(int num, struct Traingle ct)
{
struct Circle ret;
if (num==0) ret.r = 0.0;
else if (num==1)
{
ret.center = ct.p[0];
ret.r = 0.0;
}
else if (num==2)
{
ret.center.x = (ct.p[0].x+ct.p[1].x)/2.0;
ret.center.y = (ct.p[0].y+ct.p[1].y)/2.0;
ret.r = Dis(ct.p[0], ct.p[1])/2.0;
}
else if(num==3) ret = CircumCircle(ct);
return ret;
}
//递归实现增量算法
void Dfs(int x, int num, struct Traingle ct)
{
int i, j;
struct Point tmp;
ans = MinCircle(num, ct);
if (num==3) return;
for (i=1; i<=x; i++)
if (Dis(pt[i], ans.center)>ans.r)
{
ct.p[num]=pt[i];
Dfs(i-1, num+1, ct);
tmp=pt[i];
for (j=i;j>=2;j--)
pt[j]=pt[j-1];
pt[1]=tmp;
}
}
void Solve(int n)
{
struct Traingle ct;
Dfs(n, 0, ct);
}
int main (void)
{
int n, i;
while (scanf("%d", &n)!=EOF && n)
{
for (i=1;i<=n;i++)
scanf("%lf %lf", &pt[i].x, &pt[i].y);
Solve(n);
printf("%.2lf %.2lf %.2lf\n", ans.center.x, ans.center.y, ans.r);
}
return 0;
}
求两个圆的交点
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
const double eps = 1e-8;
const double PI = acos(-1.0);
struct Point
{
double x;
double y;
};
typedef struct Point point;
struct Line
{
double s, t;
};
typedef struct Line Line;
struct Circle
{
Point center;
double r;
Line line[505];
int cnt;
bool covered;
}circle[105];
double distance(point p1, point p2)
{
double dx = p1.x-p2.x;
double dy = p1.y-p2.y;
return sqrt(dx*dx + dy*dy);
}
point intersection(point u1,point u2, point v1,point v2)
{
point ret = u1;
double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x)) /
((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
ret.x += (u2.x-u1.x)*t;
ret.y += (u2.y-u1.y)*t;
return ret;
}
void intersection_line_circle(point c,double r,point l1,point l2,point& p1,point& p2)
{
point p=c;
double t;
p.x+=l1.y-l2.y;
p.y+=l2.x-l1.x;
p=intersection(p,c,l1,l2);
t=sqrt(r*r-distance(p,c)*distance(p,c))/distance(l1,l2);
p1.x=p.x+(l2.x-l1.x)*t;
p1.y=p.y+(l2.y-l1.y)*t;
p2.x=p.x-(l2.x-l1.x)*t;
p2.y=p.y-(l2.y-l1.y)*t;
}
//计算圆与圆的交点,保证圆与圆有交点,圆心不重合
void intersection_circle_circle(point c1,double r1,point c2,double r2,point& p1,point& p2)
{
point u,v;
double t;
t=(1+(r1*r1-r2*r2)/distance(c1,c2)/distance(c1,c2))/2;
u.x=c1.x+(c2.x-c1.x)*t;
u.y=c1.y+(c2.y-c1.y)*t;
v.x=u.x+c1.y-c2.y;
v.y=u.y-c1.x+c2.x;
intersection_line_circle(c1,r1,u,v,p1,p2);
}
求三角形外接圆圆心
struct Point
{
double x;
double y;
}pt[1005];
struct Traingle
{
struct Point p[3];
};
struct Circle
{
struct Point center;
double r;
}ans;
//计算两点距离
double Dis(struct Point p, struct Point q)
{
double dx=p.x-q.x;
double dy=p.y-q.y;
return sqrt(dx*dx+dy*dy);
}
//计算三角形面积
double Area(struct Traingle ct)
{
return fabs((ct.p[1].x-ct.p[0].x)*(ct.p[2].y-ct.p[0].y)-(ct.p[2].x-ct.p[0].x)*(ct.p[1].y-ct.p[0].y))/2.0;
}
//求三角形的外接圆,返回圆心和半径(存在结构体"圆"中)
struct Circle CircumCircle(struct Traingle t)
{
struct Circle tmp;
double a, b, c, c1, c2;
double xA, yA, xB, yB, xC, yC;
a = Dis(t.p[0], t.p[1]);
b = Dis(t.p[1], t.p[2]);
c = Dis(t.p[2], t.p[0]);
//根据S = a * b * c / R / 4;求半径R
tmp.r = (a*b*c)/(Area(t)*4.0);
xA = t.p[0].x;
yA = t.p[0].y;
xB = t.p[1].x;
yB = t.p[1].y;
xC = t.p[2].x;
yC = t.p[2].y;
c1 = (xA*xA+yA*yA - xB*xB-yB*yB) / 2;
c2 = (xA*xA+yA*yA - xC*xC-yC*yC) / 2;
tmp.center.x = (c1*(yA - yC)-c2*(yA - yB)) / ((xA - xB)*(yA - yC)-(xA - xC)*(yA - yB));
tmp.center.y = (c1*(xA - xC)-c2*(xA - xB)) / ((yA - yB)*(xA - xC)-(yA - yC)*(xA - xB));
return tmp;
}
求凸包
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define INF 999999999.9
#define PI acos(-1.0)
struct Point
{
double x, y, dis;
}pt[1005], stack[1005], p0;
int top, tot;
//计算几何距离
double Dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
//极角比较, 返回-1: p0p1在p0p2的右侧,返回0:p0,p1,p2共线
int Cmp_PolarAngel(struct Point p1, struct Point p2, struct Point pb)
{
double delta=(p1.x-pb.x)*(p2.y-pb.y)-(p2.x-pb.x)*(p1.y-pb.y);
if (delta<0.0) return 1;
else if (delta==0.0) return 0;
else return -1;
}
// 判断向量p2p3是否对p1p2构成左旋
bool Is_LeftTurn(struct Point p3, struct Point p2, struct Point p1)
{
int type=Cmp_PolarAngel(p3, p1, p2);
if (type<0) return true;
return false;
}
//先按极角排,再按距离由小到大排
int Cmp(const void*p1, const void*p2)
{
struct Point*a1=(struct Point*)p1;
struct Point*a2=(struct Point*)p2;
int type=Cmp_PolarAngel(*a1, *a2, p0);
if (type<0) return -1;
else if (type==0)
{
if (a1->dis<a2->dis) return -1;
else if (a1->dis==a2->dis) return 0;
else return 1;
}
else return 1;
}
//求凸包
void Solve(int n)
{
int i, k;
p0.x=p0.y=INF;
for (i=0;i<n;i++)
{
scanf("%lf %lf",&pt[i].x, &pt[i].y);
if (pt[i].y < p0.y)
{
p0.y=pt[i].y;
p0.x=pt[i].x;
k=i;
}
else if (pt[i].y==p0.y)
{
if (pt[i].x<p0.x)
{
p0.x=pt[i].x;
k=i;
}
}
}
pt[k]=pt[0];
pt[0]=p0;
for (i=1;i<n;i++)
pt[i].dis=Dis(pt[i].x,pt[i].y, p0.x,p0.y);
qsort(pt+1, n-1, sizeof(struct Point), Cmp);
//去掉极角相同的点
tot=1;
for (i=2;i<n;i++)
if (Cmp_PolarAngel(pt[i], pt[i-1], p0))
pt[tot++]=pt[i-1];
pt[tot++]=pt[n-1];
//求凸包
top=1;
stack[0]=pt[0];
stack[1]=pt[1];
for (i=2;i<tot;i++)
{
while (top>=1 && Is_LeftTurn(pt[i], stack[top], stack[top-1])==false)
top--;
stack[++top]=pt[i];
}
}
int main (void)
{
int n;
while (scanf("%d",&n)==2)
{
Solve(n);
}
return 0;
}
凸包卡壳旋转求出所有对踵点、最远点对
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define INF 999999999.9
#define PI acos(-1.0)
struct Point
{
double x, y, dis;
}pt[6005], stack[6005], p0;
int top, tot;
//计算几何距离
double Dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
//极角比较, 返回-1: p0p1在p0p2的右侧,返回0:p0,p1,p2共线
int Cmp_PolarAngel(struct Point p1, struct Point p2, struct Point pb)
{
double delta=(p1.x-pb.x)*(p2.y-pb.y)-(p2.x-pb.x)*(p1.y-pb.y);
if (delta<0.0) return 1;
else if (delta==0.0) return 0;
else return -1;
}
// 判断向量p2p3是否对p1p2构成左旋
bool Is_LeftTurn(struct Point p3, struct Point p2, struct Point p1)
{
int type=Cmp_PolarAngel(p3, p1, p2);
if (type<0) return true;
return false;
}
//先按极角排,再按距离由小到大排
int Cmp(const void*p1, const void*p2)
{
struct Point*a1=(struct Point*)p1;
struct Point*a2=(struct Point*)p2;
int type=Cmp_PolarAngel(*a1, *a2, p0);
if (type<0) return -1;
else if (type==0)
{
if (a1->dis<a2->dis) return -1;
else if (a1->dis==a2->dis) return 0;
else return 1;
}
else return 1;
}
//求凸包
void Hull(int n)
{
int i, k;
p0.x=p0.y=INF;
for (i=0;i<n;i++)
{
scanf("%lf %lf",&pt[i].x, &pt[i].y);
if (pt[i].y < p0.y)
{
p0.y=pt[i].y;
p0.x=pt[i].x;
k=i;
}
else if (pt[i].y==p0.y)
{
if (pt[i].x<p0.x)
{
p0.x=pt[i].x;
k=i;
}
}
}
pt[k]=pt[0];
pt[0]=p0;
for (i=1;i<n;i++)
pt[i].dis=Dis(pt[i].x,pt[i].y, p0.x,p0.y);
qsort(pt+1, n-1, sizeof(struct Point), Cmp);
//去掉极角相同的点
tot=1;
for (i=2;i<n;i++)
if (Cmp_PolarAngel(pt[i], pt[i-1], p0))
pt[tot++]=pt[i-1];
pt[tot++]=pt[n-1];
//求凸包
top=1;
stack[0]=pt[0];
stack[1]=pt[1];
for (i=2;i<tot;i++)
{
while (top>=1 && Is_LeftTurn(pt[i], stack[top], stack[top-1])==false)
top--;
stack[++top]=pt[i];
}
}
//计算叉积
double CrossProduct(struct Point p1, struct Point p2, struct Point p3)
{
return (p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y);
}
//卡壳旋转,求出凸多边形所有对踵点
void Rotate(struct Point*ch, int n)
{
int i, p=1;
double t1, t2, ans=0.0, dif;
ch[n]=ch[0];
for (i=0;i<n;i++)
{
//如果下一个点与当前边构成的三角形的面积更大,则说明此时不构成对踵点
while (fabs(CrossProduct(ch[i],ch[i+1],ch[p+1])) > fabs(CrossProduct(ch[i],ch[i+1],ch[p])))
p=(p+1)%n;
dif=fabs(CrossProduct(ch[i],ch[i+1],ch[p+1])) - fabs(CrossProduct(ch[i],ch[i+1],ch[p]));
//如果当前点和下一个点分别构成的三角形面积相等,则说明两条边即为平行线,对角线两端都可能是对踵点
if (dif==0.0)
{
t1=Dis(ch[p].x, ch[p].y, ch[i].x, ch[i].y);
t2=Dis(ch[p+1].x, ch[p+1].y, ch[i+1].x, ch[i+1].y);
if (t1>ans)ans=t1;
if (t2>ans)ans=t2;
}
//说明p,i是对踵点
else if (dif<0.0)
{
t1=Dis(ch[p].x, ch[p].y, ch[i].x, ch[i].y);
if (t1>ans)ans=t1;
}
}
printf("%.2lf\n",ans);
}
int main (void)
{
int n;
while (scanf("%d",&n)==1)
{
Hull(n);
Rotate(stack, top+1);
}
return 0;
}
凸包+旋转卡壳求平面面积最大三角
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define INF 99999999999.9
#define PI acos(-1.0)
struct Point
{
double x, y, dis;
}pt[50005], stack[50005], p0;
int top, tot;
double Dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int Cmp_PolarAngel(struct Point p1, struct Point p2, struct Point pb)
{
double delta=(p1.x-pb.x)*(p2.y-pb.y)-(p2.x-pb.x)*(p1.y-pb.y);
if (delta<0.0) return 1;
else if (delta==0.0) return 0;
else return -1;
}
bool Is_LeftTurn(struct Point p3, struct Point p2, struct Point p1)
{
int type=Cmp_PolarAngel(p3, p1, p2);
if (type<0) return true;
return false;
}
int Cmp(const void*p1, const void*p2)
{
struct Point*a1=(struct Point*)p1;
struct Point*a2=(struct Point*)p2;
int type=Cmp_PolarAngel(*a1, *a2, p0);
if (type<0) return -1;
else if (type==0)
{
if (a1->dis<a2->dis) return -1;
else if (a1->dis==a2->dis) return 0;
else return 1;
}
else return 1;
}
void Hull(int n)
{
int i, k;
p0.x=p0.y=INF;
for (i=0;i<n;i++)
{
scanf("%lf %lf",&pt[i].x, &pt[i].y);
if (pt[i].y < p0.y)
{
p0.y=pt[i].y;
p0.x=pt[i].x;
k=i;
}
else if (pt[i].y==p0.y)
{
if (pt[i].x<p0.x)
{
p0.x=pt[i].x;
k=i;
}
}
}
pt[k]=pt[0];
pt[0]=p0;
for (i=1;i<n;i++)
pt[i].dis=Dis(pt[i].x,pt[i].y, p0.x,p0.y);
qsort(pt+1, n-1, sizeof(struct Point), Cmp);
tot=1;
for (i=2;i<n;i++)
if (Cmp_PolarAngel(pt[i], pt[i-1], p0))
pt[tot++]=pt[i-1];
pt[tot++]=pt[n-1];
top=1;
stack[0]=pt[0];
stack[1]=pt[1];
for (i=2;i<tot;i++)
{
while (top>=1 && Is_LeftTurn(pt[i], stack[top], stack[top-1])==false)
top--;
stack[++top]=pt[i];
}
}
double TArea(struct Point p1, struct Point p2, struct Point p3)
{
return fabs((p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y));
}
void Rotate(struct Point*ch, int n)
{
if (n<3)
{
printf("0.00\n");
return;
}
int i, j, k;
double ans=0.0, tmp;
ch[n]=ch[0];
for (i=0;i<n;i++)
{
j=(i+1)%n;
k=(j+1)%n;
while ((j!=k) && (k!=i))
{
while (TArea(ch[i],ch[j],ch[k+1])>TArea(ch[i],ch[j],ch[k]))
k=(k+1)%n;
tmp=TArea(ch[i],ch[j], ch[k]);
if (tmp>ans) ans=tmp;
j=(j+1)%n;
}
}
printf("%.2lf\n",ans/2.0);
}
int main (void)
{
int n;
while (scanf("%d",&n)==1)
{
if (n==-1)break;
Hull(n);
Rotate(stack, top+1);
}
return 0;
}
Pick定理
// Pick定理求整点多边形内部整点数目
// (1) 给定顶点座标均是整点(或正方形格点)的简单多边形,皮克定理说明了其面积A和内部格点数目i、边上格点数目b的关系:A = i + b/2 - 1;
// (2) 在两点(x1,y1),(x2,y2)连线之间的整点个数(包含一个端点)为:gcd(|x1-x2|,|y1-y2|);
// (3) 求三角形面积用叉乘
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
long long x[3], y[3], area, b;
long long My_Abs(long long t)
{
if (t<0) return -t;
return t;
}
long long Gcd(long long x, long long y)
{
if (y==0) return x;
long long mod=x%y;
while (mod)
{
x=y;
y=mod;
mod=x%y;
}
return y;
}
int main (void)
{
int i;
while (1)
{
for (i = 0;i < 3;i ++)
scanf("%lld %lld", &x[i], &y[i]);
if(x[0]==0&&y[0]==0&&x[1]==0&&y[1]==0&&x[2]==0&&y[2]==0) break;
area = (x[1]-x[0])*(y[2]-y[0])-(x[2]-x[0])*(y[1]-y[0]);
area = My_Abs(area);
b=0;
b=Gcd(My_Abs(x[1]-x[0]), My_Abs(y[1]-y[0])) + Gcd(My_Abs(x[2]-x[0]), My_Abs(y[2]-y[0])) + Gcd(My_Abs(x[1]-x[2]), My_Abs(y[1]-y[2]));
printf("%lld\n", (area-b+2)/2);
}
return 0;
}
求多边形面积和重心
#include <stdio.h>
#include <math.h>
int x[1000003], y[1000003];
double A, tx, ty, tmp;
int main (void)
{
int cases, n, i;
scanf ("%d", &cases);
while (cases --)
{
scanf ("%d", &n);
A = 0.0;
x[0] = y[0] = 0;
for (i = 1; i <= n; i ++)
{
scanf ("%d %d", &x[i], &y[i]);
A += (x[i-1]*y[i] - x[i]*y[i-1]);
}
A += x[n]*y[1] - x[1]*y[n];
A = A / 2.0;
tx = ty = 0.0;
for (i = 1; i < n; i ++)
{
tmp = x[i]*y[i+1] - x[i+1]*y[i];
tx += (x[i]+x[i+1]) * tmp;
ty += (y[i]+y[i+1]) * tmp;
}
tmp = x[n]*y[1] - x[1]*y[n];
tx += (x[n]+x[1])*tmp;
ty += (y[n]+y[1])*tmp;
printf ("%.2lf %.2lf\n", tx/(6.0*A), ty/(6.0*A));
}
return 0;
}
蚁群退火算法
Fish’s mission 也就是求一个坐标到各个食堂的距离和最小,随机化做应该也是可以的。标程用的方法是利用单调性,不断尝试四个方向,二分的方法做的。
#include <cmath>
#include <cstdio>
#define pi acos(-1.0)
struct Point{
double x,y;
Point(){}
Point(double a,double b){
x=a,y=b;
}
}point[101];
double pt_distance(const Point &p1,const Point &p2){
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
double get_all_dis(const Point &p,int n){
double ans=0.0;
for (int i=0; i<n; i++) ans+=pt_distance(point[i],p);
return ans;
}
int main() {
int n;
while (~scanf("%d",&n)){
for (int i=0; i<n; i++)
scanf("%lf%lf",&point[i].x,&point[i].y);
Point st=point[0];
double step=100,mind=get_all_dis(st,n);
while (step>0.0002){
int ok=1;
while (ok){
Point tmp,nt;
double t;
ok=0,nt=st;
tmp=Point(st.x,st.y+step);
t=get_all_dis(tmp,n);
if (t<mind) mind=t,ok=1,nt=tmp;
tmp=Point(st.x,st.y-step);
t=get_all_dis(tmp,n);
if (t<mind) mind=t,ok=1,nt=tmp;
tmp=Point(st.x+step,st.y);
t=get_all_dis(tmp,n);
if (t<mind) mind=t,ok=1,nt=tmp;
tmp=Point(st.x-step,st.y);
t=get_all_dis(tmp,n);
if (t<mind) mind=t,ok=1,nt=tmp;
st=nt;
}
step=step/2.0;
}
printf("%.3lf\n",mind);
}
return 0;
}
模拟退火
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define Lim 0.999999
#define EPS 1e-2
#define PI acos(-1.0)
double Temp, maxx, minx, maxy, miny, lx, ly, dif;
int nt, ns, nc;
struct Target
{
double x, y;
}T[105];
struct Solution
{
double x, y;
double f;
}S[25], P, A;
double Dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void Seed(void)
{
int i, j;
for (i=0;i<ns;i++)
{
S[i].x=minx+((double)(rand()%1000+1)/1000.0)*lx;
S[i].y=miny+((double)(rand()%1000+1)/1000.0)*ly;
S[i].f=0.0;
for (j=0;j<nt;j++)
S[i].f=S[i].f+Dis(S[i].x,S[i].y, T[j].x, T[j].y);
}
}
void Trans(void)
{
int i, j, k;
double theta;
for (i=0;i<ns;i++)
{
P=S[i];
for (j=0;j<nc;j++)
{
theta=(((double)(rand()%1000+1))/1000.0)*2.0*PI;
A.x=P.x+Temp*cos(theta);
A.y=P.y+Temp*sin(theta);
if (A.x<minx||A.x>maxx||A.y<miny||A.y>maxy)
continue;
A.f=0.0;
for (k=0;k<nt;k++)
A.f=A.f+Dis(A.x,A.y,T[k].x,T[k].y);
dif=A.f-S[i].f;
if (dif<0.0)S[i]=A;
else
{
dif=exp(-dif/Temp);
if (dif>Lim) S[i]=A;
}
}
}
}
int main (void)
{
int i, k;
while (scanf("%d",&nt)==1&&nt)
{
maxx=maxy=0;
minx=miny=(1<<20);
for (i=0;i<nt;i++)
{
scanf("%lf %lf",&T[i].x,&T[i].y);
if (maxx<T[i].x)maxx=T[i].x;
if (minx>T[i].x)minx=T[i].x;
if (maxy<T[i].y)maxy=T[i].y;
if (miny>T[i].y)miny=T[i].y;
}
lx=maxx-minx;
ly=maxy-miny;
Temp=sqrt(lx*lx+ly*ly)/3.0;
ns=5, nc=10;
Seed();
while (Temp>EPS)
{
Trans();
Temp=Temp*0.40;
}
k=0;
for (i=1;i<ns;i++)
if (S[k].f>S[i].f)
k=i;
printf ("%.0lf\n", S[k].f);
}
return 0;
}
用一个给定半径的圆覆盖最多的点
//同半径圆的圆弧表示
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define PI acos(-1.0)
struct Point
{
double x, y;
}pt[2005];
double dis[2005][2005];
struct List
{
double a;
bool flag;
int id;
}list[8005];
int cnt;
double Dis(int i, int j)
{
double dx=pt[i].x-pt[j].x;
double dy=pt[i].y-pt[j].y;
return sqrt(dx*dx+dy*dy);
}
int Cmp(const void*p1, const void*p2)
{
struct List*a1=(struct List*)p1;
struct List*a2=(struct List*)p2;
if (a1->a<a2->a)return -1;
else if (a1->a==a2->a) return a1->id-a2->id;
else return 1;
}
int main (void)
{
int n, i, j, ans, num;
double r, theta, delta, a1, a2;
while (scanf("%d %lf",&n,&r)==2)
{
if (n==0&&r==0.0) break;
r=r+0.001;
r=r*2.0;
for (i=1;i<=n;i++)
scanf("%lf %lf", &pt[i].x, &pt[i].y);
for (i=1;i<n;i++)
for (j=i+1;j<=n;j++)
{
dis[i][j]=Dis(i, j);
dis[j][i]=dis[i][j];
}
ans=0;
for (i=1;i<=n;i++)
{
cnt=0;
for (j=1;j<=n;j++)
if ((j!=i)&&(dis[i][j]<=r))
{
theta=atan2(pt[j].y-pt[i].y, pt[j].x-pt[i].x);
if (theta<0.0) theta=theta+2.0*PI;
delta=acos(dis[i][j]/r);
a1=theta-delta;
a2=theta+delta;
list[++cnt].a=a1;
list[cnt].flag=true;
list[cnt].id=cnt;
list[++cnt].a=a2;
list[cnt].flag=false;
list[cnt].id=cnt;
}
qsort(list+1,cnt,sizeof(struct List),Cmp);
num=0;
for (j=1;j<=cnt;j++)
if (list[j].flag)
{
num++;
if (num>ans) ans=num;
}
else num--;
}
printf("It is possible to cover %d points.\n", ans+1);
}
return 0;
}
最近圆对
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<set>
#include <math.h>
using namespace std;
set <int>tree;
set <int>::iterator iter;
struct Point
{
double x;
int id, flag;
}p1[100001], p2[100001];
int tot1, tot2;
struct Q
{
double x,y, r;
}q[50001];
int cmp(const void*p1, const void*p2)
{
struct Point*a1=(struct Point*)p1;
struct Point*a2=(struct Point*)p2;
if (a1->x<a2->x) return -1;
else if (a1->x==a2->x) return a2->flag-a1->flag;
else return 1;
}
int cmp1(const void*p1, const void*p2)
{
struct Q*a1=(struct Q*)p1;
struct Q*a2=(struct Q*)p2;
if (a1->y<a2->y)return -1;
else if (a1->y==a2->y)return 0;
else return 1;
}
double dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
bool judge(int i, int j, double d)
{
if (dis(q[i].x, q[i].y, q[j].x, q[j].y)<=q[i].r+q[j].r+2.0*d)
return true;
return false;
}
bool insert(int v,double d)
{
iter = tree.insert(v).first;
if (iter != tree.begin())
{
if (judge(v, *--iter,d))
{
return true;
}
++iter;
}
if (++iter != tree.end())
{
if (judge(v, *iter,d))
{
return true;
}
}
return false;
}
bool remove(int v,double d)
{
iter = tree.find(v);
if (iter != tree.begin() && iter != --tree.end())
{
int a = *--iter;
++iter;
int b = *++iter;
if (judge(a, b,d))
{
return true;
}
}
tree.erase(v);
return false;
}
bool check(double d)
{
int i=1, j=1;
while (i<=tot1&&j<=tot2)
{
if (p1[i].x-d<=p2[j].x+d)
{
if (insert(p1[i++].id, d))
return true;
}
else
{
if (remove(p2[j++].id, d))
return true;
}
}
while (i<=tot1)
{
if (insert(p1[i++].id, d))
return true;
}
while (j<=tot2)
{
if (remove(p2[j++].id, d))
return true;
}
return false;
}
int main (void)
{
int cases, n, i;
scanf("%d",&cases);
while (cases--)
{
scanf("%d",&n);
tot1=tot2=0;
for (i=1;i<=n;i++)
scanf("%lf %lf %lf",&q[i].x,&q[i].y, &q[i].r);
qsort(q+1,n,sizeof(struct Q),cmp1);
for (i=1;i<=n;i++)
{
tot1++;
p1[tot1].x=q[i].x-q[i].r;
p1[tot1].id=i;
p1[tot1].flag=1;
tot2++;
p2[tot2].x=q[i].x+q[i].r;
p2[tot2].id=i;
p2[tot2].flag=-1;
}
qsort(p1+1,tot1,sizeof(struct Point),cmp);
qsort(p2+1,tot2,sizeof(struct Point),cmp);
double head=0.0, tail=dis(q[1].x,q[1].y,q[2].x,q[2].y)+1.0, mid;
while (tail-head>1e-8)
{
tree.clear();
mid=(head+tail)/2.0;
if (check(mid))
{
tail=mid;
}
else head=mid;
}
printf ("%.6lf\n",2.0*head);
}
return 0;
}
求两个圆的面积交
double area_of_overlap(point c1, double r1, point c2, double r2)
{
double a = distance(c1, c2), b = r1, c = r2;
double cta1 = acos((a * a + b * b - c * c) / 2 / (a * b)),
cta2 = acos((a * a + c * c - b * b) / 2 / (a * c));
double s1 = r1*r1*cta1 - r1*r1*sin(cta1)*(a * a + b * b - c * c) / 2 / (a * b);
double s2 = r2*r2*cta2 - r2*r2*sin(cta2)*(a * a + c * c - b * b) / 2 / (a * c);
return s1 + s2;
}
判断线段相交
(1)模板1
struct Point
{
double x,y;
};
struct LineSeg
{
Point a,b;
}L1,L2;
double multiply(Point p1,Point p2,Point p0)
{
return ((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}
int intersect(LineSeg u,LineSeg v)
{
return( (max(u.a.x,u.b.x)>=min(v.a.x,v.b.x))&&
(max(v.a.x,v.b.x)>=min(u.a.x,u.b.x))&&
(max(u.a.y,u.b.y)>=min(v.a.y,v.b.y))&&
(max(v.a.y,v.b.y)>=min(u.a.y,u.b.y))&&
(multiply(v.a,u.b,u.a)*multiply(u.b,v.b,u.a)>=0)&&
(multiply(u.a,v.b,v.a)*multiply(v.b,u.b,v.a)>=0));
}
模板2
int det(const Point &a, const Point &b) {
return a.x * b.y - b.x * a.y;
}
Point operator -(const Point &a, const Point &b) {
return Point(a.x - b.x, a.y - b.y);
}
bool cross(const Point &a, const Point &b, const Point &c, const Point &d) {
if ((det(c - a, b - a) > 0) ^ (det(b - a, d - a) > 0))
return false;
if ((det(a - c, d - c) > 0) ^ (det(d - c, b - c) > 0))
return false;
return true;
}
平面分成区域数
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
using namespace std;
const double eps = 1e-10;
struct Point
{
double x,y;
Point(double x=0,double y=0):x(x),y(y) { }
bool operator < (const Point& a) const
{
if(a.x != x) return x < a.x;
return y < a.y;
}
};
typedef Point Vector;
const int maxn = 310;
Point P[maxn], V[maxn*maxn];
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;
}
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 * (Point A,double P)
{
return Vector(A.x *P , A.y*P);
}
double Cross(Vector A,Vector B)
{
return A.x*B.y-A.y*B.x;
}
double Dot(Vector A ,Vector B)
{
return A.x * B.x + A.y * B.y;
}
bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)
{
double c1 = Cross(a2-a1,b1-a1),c2 = Cross(a2-a1,b2-b1);
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;
}
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;
}
int main()
{
int n,kase =0;
while(scanf("%d",&n) &&n)
{
for(int i=0; i<n; i++)
{
scanf("%lf %lf",&P[i].x,&P[i].y);
V[i]=P[i];
}
n--;
int c=n,e=n;
for(int i=0; i<n; i++)
for(int j=i+1; j<n; j++)
if(SegmentProperIntersection(P[i],P[i+1],P[j],P[j+1]))
V[c++] = GetLineIntersection(P[i],P[i+1]-P[i],P[j],P[j+1]-P[j]);
sort(V,V+c);
c=unique(V,V+c) -V;
for(int i=0; i<c; i++)
for(int j=0; j<n; j++)
if(OnSegment(V[i],P[j],P[j+1])) e++;
printf("Case %d: There are %d pieces.\n",++kase,e+2-c);
}
return 0;
}
图论
tarjan算法
#include<iostream>
using namespace std;
#define MAXN 10000//最多的点数
#define MAXM 10000//最多的边数
struct Node
{
int to;
int next;
}graph[MAXM];
int head[MAXN];
int n, m;//顶点数,边数
int low[MAXN], dfn[MAXN];//low[i]表示能回溯到的最小节点,dfn[i]表示访问的顺序
int belong[MAXN];//belong[i]表示i属于哪一个强连通分支
bool instack[MAXN];//instack[i]表示是否在堆栈中
int stack[MAXN];
int index, num, top;//num表示有几个强连通分支
/****************************
insert()函数
输入:两个节点的标号
输出:无
功能是用链接表建立图
*****************************/
int cnt;
void insert(int u, int v)
{
graph[cnt].to = v;
graph[cnt].next = head[u];
head[u] = cnt++;
}
inline int min(int a, int b)
{
if(a<b) return a;
else return b;
}
/***************************
tarjan()函数
输入:要访问的节点
输出:无
功能:寻找强连通分量
****************************/
void tarjan(int id)
{
int i, j, e;
dfn[id] = low[id] = ++index;
instack[id] = true;
stack[top++] = id;
for(i = head[id]; i!=-1; i = graph[i].next)
{
e = graph[i].to;
if(!dfn[e])//如果未访问的话
{
tarjan(e);
low[id] = min(low[id],low[e]);//更新low值
}
else if(instack[e])
low[id] = min(low[id], dfn[e]);
}
if(dfn[id] == low[id])//寻找强连通分支的根
{
num++;//强联通分支的数量
do
{
j = stack[--top];
instack[j] = false;
belong[j] = num;
}while(j!=id);
}
}
int main()
{
freopen("in.txt", "r", stdin);
int i, u, v, j;
while(scanf("%d %d", &n, &m)!=EOF)
{
for(i=0; i<=n; i++)//初始化
{
head[i] = -1;
dfn[i] = 0;
low[i] = 0;
instack[i] = false;
}
index = cnt = num = top = 0;
for(i=0; i<m; i++)
{
scanf("%d %d", &u, &v);
insert(u, v);
}
for(i=1; i<=n; i++)
if(!dfn[i])
tarjan(i);
printf("总共有几个强连通分支:%d\n",num);
for(i=1; i<=num; i++)
{
printf("第%d个强连通分支是:", i);
for(j=1; j<=n; j++)
if(belong[j] == i)
printf("%d ",j);
printf("\n");
}
}
return 0;
}
图 论
网络流
1点离散化
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=10010;
int n,m;
int head[N],dep[N],idx;
int src,des;
struct Edge{
int to,cap;
int nxt;
}edge[1010];
void Init(){
idx=0;
memset(head,-1,sizeof(head));
}
void addedge(int cu,int cv,int cw){
edge[idx].to=cv;
edge[idx].cap=cw;
edge[idx].nxt=head[cu];
head[cu]=idx++;
edge[idx].to=cu;
edge[idx].cap=0;
edge[idx].nxt=head[cv];
head[cv]=idx++;
}
bool BFS(){
queue<int> q;
while(!q.empty())
q.pop();
memset(dep,-1,sizeof(dep));
dep[src]=0;
q.push(src);
while(!q.empty()){
int pos=q.front();
q.pop();
for(int i=head[pos];i!=-1;i=edge[i].nxt)
if(edge[i].cap>0 && dep[edge[i].to]==-1){
dep[edge[i].to]=dep[pos]+1;
q.push(edge[i].to);
}
}
return dep[des]!=-1;
}
int DFS(int u,int flow){
if(u==des)
return flow; // 结束条件,把流推到了T节点(汇点)
int res=0,f;
for(int i=head[u];i!=-1;i=edge[i].nxt)
if(dep[u]+1==dep[edge[i].to] && edge[i].cap>0
&& (f=DFS(edge[i].to,min(edge[i].cap,flow-res)))){
edge[i].cap-=f;
edge[i^1].cap+=f;
res+=f;
}
if(res==0) // tf等于零表示该点没有进行增广的能力,
应及时将其高度赋值为-1,防止第二次被搜索到
dep[u]=-1;
return res;
}
int main(){
while(~scanf("%d%d",&src,&des)){
Init();
int ans=0,m;
scanf("%d",&m);
int u,v,w;
while(m--){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
while(BFS()){
ans+=DFS(src,INF);
}
printf("%d\n",ans);
}
return 0;
}
2 EK算法
#include<iostream>
#include<queue>
using namespace std;
#define INF 0x7fffffff
#define min(a,b) ((a)<(b)?(a):(b))
int cap[201][201],flow[201][201],a[201];
//cap[][]表示最大容量,flow[][]表示当前流量
int s,t,n,m;
void ek()
{
queue<int> q;
int p[201],u,v;
memset(flow,0,sizeof(flow));
int f=0;
while(1)
{
memset(a,0,sizeof(a));
memset(p,0,sizeof(p));
a[s]=INF;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
for(v=1;v<=m;v++)
if(!a[v] && cap[u][v]>flow[u][v])
{
p[v]=u;
q.push(v);
a[v]=min(a[u],cap[u][v]-flow[u][v]);
}
}
if(a[t]==0)
break;
for(u=t;u!=s;u=p[u])
{
flow[p[u]][u]+=a[t];
flow[u][p[u]]-=a[t];
}
f+=a[t];
}
printf("%d\n",f);
}
int main()
{
// freopen("in.txt","r",stdin);
while(scanf("%d %d",&n,&m)!=EOF)
{
int i,si,ei,ci;
memset(cap,0,sizeof(cap));
for(i=0;i<n;i++)
{
scanf("%d %d %d",&si,&ei,&ci);
cap[si][ei]+=ci;
}
s=1;
t=m;
ek();
}
return 0;
}
3 ISAP算法
//题目类型:纯最大流(邻接表实现)
#include<iostream>
#include<cstdio>
//#include<conio.h>
#include<string.h>
using namespace std;
const int maxn=201;
const int maxm=201;
struct node
{
int x,y,f,op,next; //x起点,y终点,f容量,next是以x为起点的上一条边在g中的位置,op是反向边在g中的下标位置
}g[maxm*2];
//first[]存储的是以x为起点的最后一条边的在数组g中的下标
//sumd[]用于记录表示标号为i的顶点数有多少个,用于间隙优化
//now[]临时记录以x为起点的最后一条边在数组g中的下标
int first[maxn],now[maxn],sumd[maxn];
int ncount; //代表结点的总数
int dis[maxn],fanhui[maxn],pre[maxn],tot; //dis[]用于记录距离标号,pre[i]记录i的前驱在g[]中的位置,tot记录边的总数
void add(int x,int y,int c)
{
tot++; //tot记录边的总数
g[tot].x=x;
g[tot].y=y;
g[tot].f=c;
g[tot].op=tot+1; //反向边在g中的下标位置
g[tot].next=first[x]; //记录以x为起点的上一条边在g中的下标位置
first[x]=tot; //以x为起点的边的位置
tot++;
//反向边
g[tot].x=y;
g[tot].y=x;
g[tot].f=0; //反向边的初始网络流为0
g[tot].op=tot-1;
g[tot].next=first[y];
first[y]=tot;
}
//ISAP算法
int maxflow(int src,int des)
{
int i,flow,t,j,tempmin; //i,j用于标识结点,t用于标识结点在g中的位置
bool flag; //用于标识是否找到了允许路径
int sumFlow;
memset(dis,0,sizeof(dis)); //初始化dis为0
memset(sumd,0,sizeof(sumd));
for(i=1;i<=ncount;i++)
now[i]=first[i];
sumd[0]=ncount; //标号为0的结点有ncount个
sumFlow=0; //sumFlow记录最大流,初始化为0
i=src; //i初始化为起点
flow=10000000;
while(dis[src]<ncount)
{
fanhui[i]=flow;
flag=false;
t=now[i];
while(t!=0) //寻找允许路径
{
j=g[t].y;
if((g[t].f>0)&&(dis[j]+1==dis[i])) //允许弧
{
flag=true;
pre[j]=t;
now[i]=t;
if(g[t].f<flow) //找到允许增量
flow=g[t].f;
i=j;
if(i==ncount) //找到了允许路径
{
sumFlow+=flow;
while(i!=src) //修改残余网络
{
g[pre[i]].f-=flow; //正向边
g[g[pre[i]].op].f+=flow; //反向边
i=g[pre[i]].x;
}
flow=10000000;
}
break;
}
t=g[t].next;
}
if(flag)
continue;
//没有找到允许路径
tempmin=ncount-1;
t=first[i];
while(t!=0){
if((g[t].f>0)&&(dis[g[t].y]<tempmin)){
tempmin=dis[g[t].y];
now[i]=t;
}
t=g[t].next;
}
sumd[dis[i]]--;
if(sumd[dis[i]]==0) break; //间隙优化
dis[i]=tempmin+1; //此处别忘+1,因为d[i]=tempmin{d[j]+1|(i,j)在残留网络中}
sumd[dis[i]]++;
if(i!=src){
i=g[pre[i]].x;
flow=fanhui[i];
}
}
return sumFlow;
}
int main()
{
//freopen("1.txt","r",stdin);
int i,j;
int N,M;
int x,y,c;
int src,des; //src是起点,des是终点
while(scanf("%d%d",&N,&M)!=-1)
{
memset(first,0,sizeof(first)); //初始化first
src = 1;
des = M;
ncount = M;
tot = 0; //tot初始化为0
for(i=0;i<N;++i) {
cin>>x>>y>>c;
add(x,y,c);
}
printf("%d\n",maxflow(src,des));
}
return 0;
}
最小生成树
1 kruskal
#include<iostream>
#include<algorithm>
using namespace std;
#define M 100 //最多边数
#define N 100 //最多顶点数
typedef struct edge
{
int a;
int b;
int value;
}edge;
edge edges[M];
int final[N]; //存储父节点
int nodecount[N]; //存储该节点孩子结点的个数
bool cmp(edge a,edge b)
{
return a.value<b.value;
}
int findp(int n) //寻找父节点
{
if(final[n]==n)
return n;
else
final[n]=findp(final[n]);
return final[n];
}
bool Union(int x,int y) //合并
{
int rootx=findp(x);
int rooty=findp(y);
if(rootx==rooty)
return false;
else if(nodecount[rootx]<=nodecount[rooty])
{
final[rootx]=rooty;
nodecount[rooty]+=nodecount[rootx];
}
else
{
final[rooty]=rootx;
nodecount[rootx]+=nodecount[rooty];
}
return true;
}
int main ()
{
//freopen("1.txt","r",stdin);
int num=0;
int n,m,sum;
int i;
while ( scanf ( "%d%d", &n, &m ) != EOF )
{
num=0; //记录生成树中的边的数目
sum=0;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&edges[i].a,&edges[i].b,&edges[i].value);
}
for(i=1;i<=n;i++) //初始化
{
final[i]=i;
nodecount[i]=1;
}
sort(edges+1,edges+m+1,cmp); //排序
for(i=1;i<=m;i++) //遍历所有的边
{
if(Union(edges[i].a,edges[i].b)) //合并
{
num++;//记录最小生成树的边数
sum+=edges[i].value;
printf("%d->%d\n",edges[i].a,edges[i].b);
}
if(num==n-1) //找到了最小生成树
break;
}
printf("%d\n",sum);
}
return 0;
}
2 prim
#include<iostream>
using namespace std;
#define M 100 //顶点数
#define INF 0x7fffffff
int graph[M][M]; //用于存放图结构
bool visit[M]; //用于标志是否与已标记
int lowcost[M]; //标记最小边
int closest[M];
int n; //结点个数
int prim(int src)
{
int sum = 0;
int minData;
int i,j;
int v;
memset(visit,0,sizeof(visit));
for(i=1;i<=n;++i)
{
lowcost[i]=graph[src][i];
closest[i]=src;
}
lowcost[src] = 0; //初始化源点的最短距离为0
visit[src] = true;
for(i=1;i<n;++i)
{
minData = INF;
for(j=1;j<=n;++j)
{
if(!visit[j] && lowcost[j]<minData)
{
v = j;
minData = lowcost[j];
}
}
visit[v] = true;
sum +=lowcost[v];
for(j=1;j<=n;++j)
{
if(!visit[j] && graph[v][j]<INF && lowcost[j]>graph[v][j]) //prim算法仅在此处与Dijkstra算法不同
{
lowcost[j] =graph[v][j];
closest[j]=v;
}
}
}
return sum;
}
int main()
{
// freopen("in.txt","r",stdin);
int i,j,m,k,w;
while(scanf("%d",&n)!=EOF)
{
scanf("%d",&m);//输入边数
for(i=1;i<=n;i++)//初始化为最大值
for(j=1;j<=n;j++)
graph[i][j]=INF;
for(i=1;i<=m;i++)
{
scanf("%d %d %d",&j,&k,&w);
graph[j][k]=graph[k][j]=w;
}
int minsum=prim(1);
for(i=2;i<=n;i++)
printf("%d->%d\n",i,closest[i]);
printf("%d\n",minsum);
}
return 0;
}
3 最优生成树
//最优比率生成树(总费用/总长度要最大)假设总费用cost/总长度len=k则cost=len*k,其中k要尽量的大,所以我们可以从k从小开始,利用二分的方法
#include<stdio.h>
#include<string.h>
#include<math.h>
#define M 1200
#define INF 0x7fffffff
#define min 1e-5
#define zero(x) (((x)>0?x:-(x))<min)
struct point
{
int x,y,h;
};
point p[M];
double mat[M][M],len[M][M];//
int cost[M][M];//两点之间的高度差
double d[M];
bool visit[M];
double prim(int u,int n)
{
int i;
double ans=0,m;
for(i=0;i<n;i++)
d[i]=INF,visit[i]=false;
d[u]=0;
while(u!=-1)
{
visit[u]=true;
ans+=d[u];
for(i=0;i<n;i++)
if(!visit[i] && mat[u][i]<d[i])
d[i]=mat[u][i];
m=INF;
u=-1;
for(i=0;i<n;i++)
if(!visit[i] && d[i]<m)
m=d[i],u=i;
}
return ans;
}
void distance(point a,point b,int i,int j)
{
double x=a.x-b.x;
double y=a.y-b.y;
len[j][i]=len[i][j]=sqrt(x*x+y*y);
cost[j][i]=cost[i][j]=abs(a.h-b.h);
}
int main()
{
int n,i,j;
while(scanf("%d",&n)!=EOF && n)
{
for(i=0; i<n; i++)
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].h);
for(i=0;i<n;i++)
for(j=i+1; j<n; j++)
distance(p[i],p[j],i,j);
double l=0.0,r=1e4,mid,ans;
while(r-l>min)//采用二分法
{
mid=(l+r)/2;
for(i=0; i<n; i++)
for(j=0; j<n; j++)
mat[i][j]=cost[i][j]-mid*len[i][j];
ans=prim(0,n);
if(zero(ans)) break;
else if(ans<0) r=mid;
Else l=mid;
}
printf("%.3lf\n",mid);
}
return 0;
}
4有向图最小生成树
//对于有向图求最小生成树
#include <algorithm>
#include <cfloat>
#include <cstdio>
using namespace std;
#define INT_MAX 0x7fffffff
const int N = 100;
bool visited[N];
int n;
double a[N][N]; //邻接矩阵
void DFS(int x)//判断是否能构成最小树形图
{
visited[x] = true;
for (int i = 0; i < n; ++i)
if (!visited[i] && a[x][i] < DBL_MAX-1)
DFS(i);
}
double ZhuYongJin_LiuZhenHong()
{
static bool flag[N];
static int prev[N];
double res = 0;
fill_n(flag, n, false);
for(;;)
{
int i;
for (i = 1; i < n; ++i)//对于每一个结点(除了根结点)求出以它为入度最小权值的边
{
if (flag[i]) continue; //该点已被删除
prev[i] = i;
a[i][i] = INT_MAX; //删除自环
for (int j = 0; j < n; ++j)
if (!flag[j] && a[j][i] < a[prev[i]][i])
prev[i] = j;
}
for (i = 1; i < n; ++i)
{
if (flag[i]) continue;
//寻找环
int j = i;
visited[0] = true;
fill_n(visited+1, n-1, false);
do
{
visited[j] = true, j = prev[j];
}while (!visited[j]);
if (!j) continue; //包含根节点
i = j;
res += a[prev[i]][i];
for (j = prev[i]; j != i; j = prev[j])
{
flag[j] = true; //删除环中除了 i 之外的所有节点
res += a[prev[j]][j];
}
//w(j, i) 减去 w(prev(i), i)
for (j = 0; j < n; ++j)
if (!flag[j] && a[j][i] < DBL_MAX-1)
a[j][i] -= a[prev[i]][i];
//修改和环关联的边的权值
for (int k = 0; k < n; ++k)
if (!flag[k])
for (j = prev[i]; j != i; j = prev[j])
{
if (a[j][k] < a[i][k])
a[i][k] = a[j][k];
if (a[k][j] < DBL_MAX-1 && a[k][j]-a[prev[j]][j] < a[k][i])
a[k][i] = a[k][j]-a[prev[j]][j];
}
break;
}
if (i == n) //不再存在环
{
for (i = 1; i < n; ++i)
if (!flag[i])
res += a[prev[i]][i];
break;
}
}
return res;
}
int main()
{
int m, u, v;
double w;
while (scanf("%d%d", &n, &m) != EOF)
{
for (int i = 0; i < n; ++i)
fill_n(a[i], n, DBL_MAX);
for (i=0; i<m; i++)
{
scanf("%d%d%lf", &u, &v, &w);
a[u][v] = w;
}
fill_n(visited, n, false);
DFS(0);
for (i = 1; i < n; ++i)
if (!visited[i])
{
puts("最小树形图不存在");
goto L1;
}
printf("%lf\n", ZhuYongJin_LiuZhenHong());
L1:;
}
return 0;
}
最短路径
1 Dijkstra(邻接矩阵)
#include<iostream>
using namespace std;
#define M 100
int maxData=10000000;
int graph[M][M];
int d[M];
int pre[M]; //存储节点前驱
bool final[M];
int t,n;
void Dij(int src)
{
int i,j;
int minData;
int v;
memset(final,0,sizeof(final));
for(i=1;i<=n;++i)
{
d[i] = graph[src][i];
if(d[i]<maxData)
pre[i]=src;
else
pre[i]=0;
}
final[src]=true;
d[src]=0;
for(i=1;i<n;++i)
{
minData = maxData;
for(j=1;j<=n;++j)
{
if(!final[j] && d[j]<minData)
{
v = j;
minData=d[j];
}
}
final[v]=true;
for(j=1;j<=n;++j)
{
if(!final[j] && graph[v][j]<maxData && d[j]>d[v]+graph[v][j])
{
d[j]=d[v]+graph[v][j];
pre[j]=v; //前驱节点更新
}
}
}
cout<<d[1]<<endl; //输出所需的最短路
}
int main()
{
int i,j;
int start,end,cost;
while(cin>>t>>n)
{
for(i=1;i<=n;++i) //此处别忘了进行初始化临界矩阵为无穷大
{
for(j=1;j<=n;++j)
{
graph[i][j]=maxData;
}
}
for(i=1;i<=t;++i)
{
cin>>start>>end>>cost;
if(cost<graph[start][end]) //重边判断
{
graph[start][end]=cost;
graph[end][start]=cost;
}
}
Dij(n);
//输出最短路径(如果需要从源点到终点输出可利用栈实现)
int p = 1; //终点
while(p!=n) //源点
{
cout<<p<<"->";
p = pre[p];
}
cout<<p<<endl;
}
return 0;
}
2 flody
void flody()
{
for(k=1;k<=n;k++)//枚举中间的点
for(p=1;p<=n;p++)//起点
if(dist[p][k]>0)
{
for(q=1;q<=n;q++)//终点
if(dist[k][q]>0)
{
if(((dist[p][q]>dist[p][k]+dist[k][q]) || (dist[p][q]==0)) && (p!=q))
dist[p][q]=dist[p][k]+dist[k][q];
}
}
}
3最短路径spfa
//The Shortest Path in Nya Graph 求出a到达b点,使得每段最短的最短路
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int MAXN = 300010;
const int MAXE = MAXN * 2;
int head[MAXN];
int to[MAXE], next[MAXE], cost[MAXE];
int n, m, ecnt,c;
void init()
{
memset(head, 0, sizeof(head));
ecnt = 1;
}
inline void add_edge(int u, int v, int c)
{
to[ecnt] = v;
cost[ecnt] = c;
next[ecnt] = head[u];
head[u] = ecnt++;
}
int dis[MAXN];
int lay[MAXN];
bool vis[MAXN];
void Dijkstra(int st, int ed)
{
memset(dis, 0x7f, sizeof(dis));
memset(vis, 0, sizeof(vis));
priority_queue<PII> que;
que.push(make_pair(0, st));
dis[st] = 0;
while(!que.empty())
{
int u = que.top().second;
que.pop();
if(vis[u]) continue;
if(u == ed) return ;
vis[u] = true;
for(int p = head[u]; p; p = next[p])
{
int &v = to[p];
if(dis[v] > dis[u] + cost[p])
{
dis[v] = dis[u] + cost[p];
que.push(make_pair(-dis[v], v));
}
}
}
return ;
}
/*有n个点m条无向边,每个点有一个层次,相邻层次的点可以移动,花费为C,问1~n的最小花费。
思路:每层新建两个点a、b,i层的点到ai连一条费用为0的边,bi到i层的点连一条费用为0的边,
然后相邻的层分别从ai到bj连一条边,费用为C。跑Dijkstra+heap可AC。*/
int main()
{
int T;
scanf("%d",&T);
for(int t = 1; t <= T; ++t)
{
scanf("%d %d %d",&n,&m,&c);
init();
for(int i = 1; i <= n; ++i)
{
scanf("%d",&lay[i]);
add_edge(i, n + 2*lay[i]-1, 0);
add_edge(n + 2*lay[i], i, 0);
}
for(int i = 1; i < n; ++i)
{
add_edge(n + 2*i-1, n+2*(i+1), c);
add_edge(n + 2*(i+1)-1, n+2*i, c);
}
int u, v, w;
while(m--)
{
scanf("%d%d%d", &u, &v, &w);
add_edge(u, v, w);
add_edge(v, u, w);
}
Dijkstra(1, n);
if(dis[n] == 0x7f7f7f7f) dis[n] = -1;
printf("Case #%d: %d\n", t, dis[n]);
}
return 0;
}
4 Bellman_ford算法
//bellman-ford算法此算法适用于存在负权路径存在的情况
#include<stdio.h>
#include<string.h>
#define M 10001
#define N 101
#define inf 1000000000
struct node
{
int from, to, w;
}edge[2*M];
int d[N];
int n, m;
void bellman_ford()
{
int i, j;
for(i=1; i<=n; i++)
d[i] = inf;
d[1] = 0;
for(i=1; i<n; i++)
for(j=0; j<2*m; j++)
{
if(d[edge[j].from] + edge[j].w < d[edge[j].to])
d[edge[j].to] = d[edge[j].from] + edge[j].w;
//printf("%d%d%d%d %d\n",edge[j].from,edge[j].to,d[edge[j].from],d[edge[j].to],edge[j].w);
}
// for(i=0; i<2*m; i++)
// if(d[edge[j].from] + edge[j].w < d[edge[j].to])//存在负权路径
// return false;
// return true;
}
int main()
{
// freopen("in.txt","r",stdin);
int i, a, b, c;
while(scanf("%d %d",&n, &m))
{
if(n==0 && m==0)
break;
for(i=0; i<m; i++)
{
scanf("%d %d %d",&a, &b, &c);
edge[i].from = a;
edge[i].to = b;
edge[i].w = c;
edge[i+m].from = b;
edge[i+m].to = a;
edge[i+m].w = c;
}
bellman_ford();
// if(bellman_ford())
printf("%d\n",d[n]);
}
return 0;
}
动态规划
数位DP
将一个十进制数n = dn dn-1 ... d0,视为二进制. 即F(n) = dn*2^n + ... + d0*2^0.
给出A, B. 求0 ... B之间, 该值不大于F(A)的数的个数。
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
vector <int> num;
int dp[11][10000];
//dp状态设计成dp[pos][remain],remain表示还剩多少fA可以分配
int dfs(int pos, int remain, bool limit) //limit表示后面的数是否能乱填
{
if (pos == -1) return remain >= 5000;
if (!limit && ~dp[pos][remain]) return dp[pos][remain];
int end = limit?num[pos]:9;
int res = 0;
for (int i = 0; i <= end; i ++)
{
res += dfs(pos-1, remain-(1<<pos)*i, limit && (i == end));
}
if (!limit) dp[pos][remain] = res;
return res;
}
int main()
{
int t;
scanf("%d", &t);
memset(dp, -1,sizeof(dp));
for (int ca = 1; ca <= t; ca ++)
{
int A, B;
scanf("%d %d", &A, &B);
num.clear();
while(B)
{
num.push_back(B % 10);
B /= 10;
}
int fa = 0;
for (int i = 0; A; A /= 10, ++ i) fa += (1 << i) * (A % 10);
printf("Case #%d: %d\n", ca, dfs(num.size()-1, 5000+fa, 1));
}
return 0;
}
一维动态规划
//连个排列的LCS,排列较长
#include<stdio.h>
#include<string.h>
#define MAX 100005
//求排列date与b的最长公共子序列
int date[MAX],a[MAX],b[MAX],f[MAX];
int main()
{
int n,i,j,m,max;
while(scanf("%d",&n),n)
{
for(i=0; i<n; i++) scanf("%d",date+i);
for(i=0; i<n; i++)
{
scanf("%d",&m);
b[m]=i;
}//把b数组压缩,形成date有序
for(i=0; i<n; i++) a[b[date[i]]+1]=i+1;
a[0]=0;
max=0;
memset(f,127,sizeof(f));
f[0]=0;
for(i=1; i<=n; i++) //求最长递增子序列
{
for(j=max; f[j]>=a[i]&&j>=0; j--);
if(a[i]<f[j+1]) f[j+1]=a[i];
if(j+1>max) max=j+1;
}
printf("%d\n",max);
}
return 0;
}
二维动态规划
/* Mountain Subsequences in :4 abca out :4 */
解题思路:
DP[i][j]表示前i个字符,以j为结尾的上升序列个数,正着和反着都做一遍,然后枚举中点,两边一组合就行了。复杂度O(n*26)。其实可以降到1维,顺序扫i的时候只需要查询前面val小于val[i]的个数就可以了,然后再更新。
#include <stdio.h>
#include <string.h>
#define maxn 100005
#define MOD 2012
char ss[maxn];
int dp[maxn][30],dp2[maxn][30];
//dp[i][j]代表前i个单词权值小于等于j的上升序列方案数,dp2同理,只不过是反过来。
int main()
{
int n;
while(~scanf("%d",&n))
{
scanf("%s",ss);
memset(dp,0,sizeof(dp));
memset(dp2,0,sizeof(dp2));
for(int i=1;i<=n;++i)
{
int t=ss[i-1]-'a'+1;
dp[i][t]=1;
for(int j=1;j<t;++j)
dp[i][t]=(dp[i][t]+dp[i-1][j])%MOD;
for(int j=1;j<=26;++j)
dp[i][j]=(dp[i][j]+dp[i-1][j])%MOD;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=26;++j)
dp[i][j]=(dp[i][j]+dp[i][j-1])%MOD;
for(int i=n;i>0;--i)
{
int t=ss[i-1]-'a'+1;
dp2[i][t]=1;
for(int j=1;j<t;++j)
dp2[i][t]=(dp2[i][t]+dp2[i+1][j])%MOD;
for(int j=1;j<=26;++j)
dp2[i][j]=(dp2[i][j]+dp2[i+1][j])%MOD;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=26;++j)
dp2[i][j]=(dp2[i][j]+dp2[i][j-1])%MOD;
int ans=0;
for(int i=2;i<n;++i)
{
int t=ss[i-1]-'a'+1;
ans=(ans+dp[i-1][t-1]*dp2[i+1][t-1])%MOD;
}
printf("%d\n",ans);
}
return 0;
}
题意Alice can only take away stones in number pow of 2, say 1, 2, 4, 8, 16, ...
Bob can only take away stones in number pow of 3, say 1, 3, 9, 27, 81, ... ,Alice先扔,问最小的步数扔完
DP 写法
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10010;
const int inf = 999999999;
int dp[N][2];
int a[20], b[20];
int main()
{
a[0] = b[0] = 1;
for(int i = 1; i <= 16; i++) a[i] = a[i-1]*2, b[i] = b[i-1]*3;
int T, n;
scanf("%d", &T);
while( T-- )
{
scanf("%d", &n);
dp[0][0] = dp[0][1] = 0;
for(int i = 1; i <= n; i++)
{
int t = inf;
for(int k = 0; a[k] <= i; k++)
t = min( t, dp[ i-a[k] ][1] );
dp[i][0] = t+1;
t = inf;
for(int k = 0; b[k] <= i; k++)
t = min( t, dp[ i-b[k] ][0] );
dp[i][1] = t+1;
}
printf("%d\n", dp[n][0] );
}
return 0;
}
广搜写法
#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
#define INF 999999
int s[50022][2];
int in[50022];
int n;
int a[100]= {1,2,4,8,16};
int b[100]= {1,3,9,27};
int num2=4;
int num3=3;
queue <int> q;
void dabiao()
{
while(a[num2-1] * 2 <= 20099)
{
a[num2] = a[num2 - 1] * 2;
num2++;
}
while(b[num3-1]*3 < 20099)
{
b[num3] = b[num3-1] *3;
num3++;
}
}
void bfs()
{
while(!q.empty()) q.pop();
int j,f,t,v;
for(j=0; j<=14055; j++)
for(int k=0; k<2; k++)
s[j][k]=INF;
for(j=0; a[j]<=14011; j++)
{
s[a[j]][0] =1;
q.push(1);
q.push(a[j]);
}
while(!q.empty())
{
f=q.front();
q.pop();
t=q.front();
q.pop();
if(f)
for(j=0; t+b[j]<=14011; j++)
{
v=t+b[j];
if(s[v][1] > s[t][0]+1)
{
s[v][1] = s[t][0] +1;
q.push(0);
q.push(v);
}
}
else
for(j=0; t+a[j]<14011; j++)
{
v=t+a[j];
if(s[v][0] > s[t][1] + 1)
{
s[v][0] = s[t][1] + 1;
q.push(1);
q.push(v);
}
}
}
}
int main()
{
int t;
dabiao();
bfs();
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
printf("%d\n" ,min(s[n][0], s[n][1]));
}
return 0;
}
概率DP
/*分析:01背包的概率问题
当前的概率基于前一种状态的概率,即偷n家银行而不被抓的概率等于偷n-1家银行不被转的概率乘以偷第n家银行不被抓的概率。
用dp[i]表示偷价值为 i 时不被抓的概率,则状态转移方程为:
dp[j] = max(dp[j] , dp[j-m[i]] * (1-p[i]));
自己写关键在01背包的转换,原意是提供银行个数和期望被捕概率,然后将每个银行的钱数和逃脱概率给出,
通过将总数当作背包大小,通过求最大逃脱概率当作最大价值(但是并不是求这个),最终通过从总钱数递减找到低于期望被捕概率第一项背包,
即为不被逮捕的所能强盗的最大钱数。*/
#include <stdio.h>
#include <string.h>
int m[101];
double p[101],dp[10001];
int main()
{
int t,n,i,j,sum;
double P;
scanf("%d",&t);
while(t--)
{
scanf("%lf %d",&P,&n);
sum = 0;
P = 1 - P;
for(i = 0; i < n; i++)
{
scanf("%d %lf",&m[i],&p[i]);
sum += m[i];
p[i] = 1 - p[i];
}
memset(dp,0,sizeof(dp));
dp[0] = 1;
for(i = 0; i < n; i++)
for(j = sum; j >= m[i]; j--)
if(dp[j] < dp[j-m[i]]*p[i])
dp[j] = dp[j-m[i]]*p[i];
for(i = sum; i >= 0&&dp[i] < P; i--);
printf("%d\n",i);
}
return 0;
}
/*一个n行三角形迷宫,有概率向左,向左下,向右下移动。
求从顶点移动到最左下角的步数期望(n<45)。
比较简单的期望题,因为递推关系中无环,所以DP线性递推。
方向要搞对,从下向上,从左到右。*/
#include <stdio.h>
const int maxn=46;
double dp[maxn][maxn];
int main()
{
int n;
while(scanf("%d",&n),n)
{
double a,b,c,d,e;
scanf("%lf%lf%lf%lf%lf",&a,&b,&c,&d,&e);
dp[n][0]=0;
for(int i=1;i<=n;++i) dp[n][i]=dp[n][i-1]+1.0;
for(int i=n-1;i>0;--i)
{
dp[i][0]=a*dp[i+1][0]+b*dp[i+1][1]+1.0;
for(int j=1;j<n;++j)
dp[i][j]=c*dp[i+1][j]+d*dp[i+1][j+1]+e*dp[i][j-1]+1.0;
}
printf("%.2lf\n",dp[1][0]);
}
return 0;
}
其他杂例
三分
1 基本三分
For each test cases, there are 6 integers x1, y1, x2, y2, vr, vt in a line.
( -1000 <= x1, y1, x2, y2 <= 1000, 1 <= vr < vt <= 1000 )
(x1, y1) : the initial location of GG (x2, y2) : the destination location of GG
vr: GG's run speed vt: taxi's speed
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#define e 1e-10
using namespace std;
double x1,y11,x2,y22,vr,vt;
double cal(double x)
{
return sqrt((x1-x)*(x1-x)+y11*y11)/vr + sqrt((x2-x)*(x2-x)+y22*y22)/vt;
}
double solve()
{
double mid ,midmid;
if(x1>x2) swap(x1,x2);
double l=x1,r=x2;
while(l+e<r)
{
mid = (l+r)/2;
midmid = (mid+r)/2;
double midval,midmidval;
midval = cal(mid);
midmidval = cal(midmid);
if(midval < midmidval)
{
r=midmid;
}
else l=mid;
}
return cal(l);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lf %lf %lf %lf %lf %lf",&x1,&y11,&x2,&y22,&vr,&vt);
double ans1,ans;
ans=sqrt((x1-x2)*(x1-x2)+(y11-y22)*(y11-y22))/vr;
ans1=solve();
printf("%.2lf\n",ans<ans1?ans:ans1);
}
return 0;
}
国际网络(深搜标程)
除了两端的电脑各只连接一部电脑之外,其余的电脑都正好连接2部电脑
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#define maxn 200
int n,arr[maxn];
double x[maxn],y[maxn],minsum;
bool vis[maxn];
double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void dfs(int cur,double sum)
{
int i;
if(cur==n)//边界{
if(sum<minsum) minsum=sum;//更新最小值
return;
}
for(i=0; i<n; i++)
{
if(vis[i]) continue;//访问过的话返回继续
vis[i]=true;//标记当前坐标访问过
arr[cur]=i;//i赋给当前的结点
if(cur==0) dfs(cur+1,0);
else{
double t=dis(x[arr[cur]],y[arr[cur]],x[arr[cur-1]],y[arr[cur-1]]);
dfs(cur+1,sum+t+16);//cur+1向下一个坐标搜索,sum+t+16是累加的距离
}
vis[i]=false;//恢复原状
}
}
int main()
{
int cas=1;
int hash[200][200]= {0};
while(~scanf("%d",&n) && n)
{
memset(hash,0,sizeof(hash));
int i;
for(i=0; i<n; i++) scanf("%lf %lf",&x[i],&y[i]);
minsum=0x7fffffff;
memset(vis,0,sizeof(vis));
for(i=0; i<n; i++) arr[i]=i;
dfs(0,0);
printf("**********************************************************\n");
printf("Network #%d\n",cas++);
printf("Number of feet of cable required is %.2f.\n", minsum);
}
return 0;
}
双向广度搜索模拟
题目大意: 在一个方格里, 有一个人和一只老虎。 两者都有初始点和初始方向, 一直往前走,直到不能走。 人总是向右转, 老虎是 向左转。
第一次碰到的坐标。 如果不可能碰到 输出-1。
#include <stdio.h>
#include <string.h>
#define N 1005
bool vis1[N][N];
bool vis2[N][N];
int dir[4][2]= {0,1,1,0,0,-1,-1,0};
int T;
bool inside(int x,int y)
{
if(x>=0 &&x<T && y>=0&&y<T) return true;
return false;
}
int main()
{
int r1,c1,d1,r2,c2,d2;
int x1,y1,x2,y2;
while(scanf("%d",&T),T)
{
memset(vis1,false,sizeof(vis1));
memset(vis2,false,sizeof(vis2));
scanf("%d %d %d",&r1,&c1,&d1);
scanf("%d %d %d",&r2,&c2,&d2);
bool ok1 = true,ok2 = true;
bool flag=false;
while(1)
{
if(r1==r2 && c1==c2)
{
flag = true;
break;
}
if(!ok1 && !ok2) break;
vis1[r1][c1] = true;
vis2[r2][c2] = true;
if(ok1)
{
x1 = r1 + dir[d1][0];
y1 = c1 + dir[d1][1];
if(inside(x1,y1) && !vis1[x1][y1])
{
r1 = x1;
c1 = y1;
}
else
{
x1 = r1 + dir[(d1+1)%4][0];
y1 = c1 + dir[(d1+1)%4][1];
if(inside(x1,y1) && !vis1[x1][y1])
{
r1 = x1;
c1 = y1;
d1 = (d1+1)%4;
}
else ok1 = false;
}
}
if(ok2)
{
x2 = r2 + dir[d2][0];
y2 = c2 + dir[d2][1];
if(inside(x2,y2) && !vis2[x2][y2])
{
r2 = x2;
c2 = y2;
}
else
{
x2 = r2 + dir[(d2+3)%4][0];
y2 = c2 + dir[(d2+3)%4][1];
if(inside(x2,y2) && !vis2[x2][y2])
{
r2 = x2;
c2 = y2;
d2 = (d2+3)%4;
}
else ok2 = false;
}
}
}
if(flag) printf("%d %d\n",r1,c1);
else puts("-1");
}
return 0;
}
栈模拟ZZ的计算器
#include <stdio.h>
#include <string.h>
#include <stack>
using namespace std;
stack<long long> stk;
stack<char> op;
int main()
{
char str[100];
bool bl ;
int len, i, j, k;
long long a, b;
while (scanf("%s",str)!=EOF)
{
bl = true;
while (!stk.empty()) stk.pop();
while (!op.empty()) op.pop();
i = 0;
if (str[0] == '-') i++;
len = strlen(str);
for(; i<len; i++)
{
a = str[i++]-'0';
while(i < len && str[i] >='0' && str[i] <='9')
{
a *= 10;
a += str[i++] -'0';
}
if (!op.empty())
{
if (op.top() == '*')
{
b = stk.top();
stk.pop();
a *=b;
op.pop();
}
else if (op.top() == '/')
{
b = stk.top();
stk.pop();
if (a == 0)
{
puts("impossible");
bl = false;
break;
}
a = b / a;
op.pop();
}
}
stk.push(a);
if (i < len) op.push(str[i]);
}
if (!bl) continue;
long long sum=0;
while (!op.empty())
{
a = stk.top();
stk.pop();
if (op.top() == '+')
{
sum += a;
op.pop();
}
else
{
sum -= a;
op.pop();
}
}
if (str[0] == '-') sum -= stk.top();
else sum += stk.top();
printf("%I64d\n", sum);
}
return 0;
}
八数码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <set>
#include <cmath>
#define N 370000
#define M 876543219
using namespace std;
struct node
{
int id,z,val;
}q[N];
int num , fz , v[N] , r , l;
int f[9]={1,1,2,6,24,120,720,5040,40320};
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int p[9]={100000000,10000000,1000000,100000,10000,1000,100,10,1};
int hash(int num)
{
int i,j,n[10];
for(i=0 ; i<9 ; i++)
{
n[i]=num%10;
num/=10;
}
int c,key=0;
for(i=1;i<9;i++)
{
for(c=0,j=0 ; j<i ; j++)
if(n[j]<n[i]) c++;
key+=c*f[i];
}
return key;
}
int is_b(int x, int y)
{
if(x<0 || y<0 || x>2 || y>2)
return 1;
return 0;
}
void swap(int value , int nt , int mt , int r)
{
int t_n=nt, t_m=mt;
nt=value/p[nt]%10;
mt=value/p[mt]%10;
int t_v=value-(nt-mt)*p[t_n]+(nt-mt)*p[t_m];
int key=hash(t_v);
if(v[key]==-1)
{
v[key]=r+1;
q[l].z=r+1;
q[l].id=t_m;
q[l++].val=t_v;
}
}
void bfs ()
{
r=0;l=0;
int key=hash(num);
v[key]=0;
q[l].z=0;
q[l].id=fz;
q[l++].val=num;
while(r<l)
{
node temp=q[r++];
for(int i=0 ; i<4 ; i++)
{
int x=temp.id/3;
int y=temp.id%3;
x+=dir[i][0],y+=dir[i][1];
int t_id=x*3+y%3;
if(is_b(x,y)) continue;
swap(temp.val,temp.id,t_id,temp.z);
}
}
}
int main ()
{
memset(v,-1,sizeof(v));
num=M;fz=8;
bfs();
int t;scanf("%d",&t);
while(t--)
{
int n=0,v1;
for(int i=0 ; i<9 ; i++)
{
scanf("%d",&v1);
if(v1==0) v1=9;
n+=v1*p[i];
}
int key=hash(n);
if(v[key]==-1) puts("impossible!");
else printf("%d\n",v[key]);
}
return 0;
}
找路径上单元格最大难度值与最小值之和
#include <stdio.h>
#include <string.h>
#define maxn 101
#define INF 0x7fffffff
int G[maxn][maxn],ited[maxn][maxn];
int n,H,L,flag;
int d[4][2]= {1,0,-1,0,0,1,0,-1};
void DFS(int x,int y)
{
int i,newx,newy;
if(flag) return;
ited[x][y]=1;
if(G[x][y]<L||G[x][y]>H) return;
if(x==n&&y==n)
{
flag=1;
return;
}
for(i=0; i<4; i++)
{
newx=x+d[i][0];
newy=y+d[i][1];
if(newx>=1&&newx<=n&&newy>=1&&newy<=n&&!ited[newx][newy]) DFS(newx,newy);
}
}
int main()
{
int i,j,max,min,high,mid,low;
while(~scanf("%d",&n))
{
min=INF,max=-1;
for(i=1; i<=n; i++)
for(j=1; j<=n; j++)
{
scanf("%d",&G[i][j]);
if(G[i][j]<min) min=G[i][j];
if(G[i][j]>max) max=G[i][j];
}
//printf("min=%d max=%d\n",min,max);
high=max-min;
low=0;
while(high>=low)
{
mid=(high+low)/2;
//printf("low=%d mid=%d high=%d\n",low,mid,high);
for(i=min; i<=max-mid; i++)
{
H=i+mid;
L=i;
flag=0;
memset(ited,0,sizeof(ited));
DFS(1,1);
if(flag) break;
}
if(i<=max-mid) high=mid-1;
else low=mid+1;
}
printf("%d\n",high+1);
}
return 0;
}
字符串
Sample Input
3
{a:3,b:4,c:10,f:6}
{a:3,c:5,d:10,ee:4}
{x:1,xyz:123456789123456789123456789}
{xyz:123456789123456789123456789,x:1}
{first:1,second:2,third:3}
{third:3,second:2}
Output for Sample Input
+d,ee
-b,f
*c
No changes
#include <iostream>
#include <sstream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
void make_dict(string s, map<string,string>& m, vector<string>& keys) {
for(int i = 0; i < s.size(); i++) {
if(!isalpha(s[i]) && !isdigit(s[i])) s[i] = ' ';
}
stringstream ss(s);
string key, value;
while(ss >> key) {
ss >> value;
m[key] = value;
keys.push_back(key);
}
}
void print_list(const vector<string> v) {
for(int i = 0; i < v.size(); i++) {
if(i != 0) cout << ',';
cout << v[i];
}
cout << '\n';
}
int main() {
int T;
cin >> T;
while(T--) {
string d1, d2;
map<string,string> m1, m2;
vector<string> all, added, removed, changed;
cin >> d1 >> d2;
make_dict(d1, m1, all);
make_dict(d2, m2, all);
sort(all.begin(), all.end());
all.erase(unique(all.begin(), all.end()), all.end());
for(int i = 0; i < all.size(); i++) {
string key = all[i];
if(!m1.count(key) && m2.count(key)) added.push_back(key);
if(m1.count(key) && !m2.count(key)) removed.push_back(key);
if(m1.count(key) && m2.count(key) && m1[key] != m2[key]) changed.push_back(key);
}
int flag = 0;
if(!added.empty()) {
flag = 1;
cout << "+";
print_list(added);
}
if(!removed.empty()) {
flag = 1;
cout << "-";
print_list(removed);
}
if(!changed.empty()) {
flag = 1;
cout << "*";
print_list(changed);
}
if(!flag) cout << "No changes\n";
cout << "\n";
}
return 0;
}
递归遍历(约束条件)
Couting squre
//已知squre定义为四个顶点为1,除顶点外,1或0的个数不等
#include <cstdio>
#include <cstring>
#include <cstdlib>
const int maxn = 310;
const int inf = 1<<29;
int mat[maxn][maxn];
int sum[maxn][maxn];
int cnt,n,m;
bool check(int x1, int y1, int x2, int y2)
{
int a = sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
int b = sum[x2-1][y2-1] - sum[x1][y2-1] - sum[x2-1][y1] + sum[x1][y1];
int tmp = (y2-y1+1)*2 + (x2-x1+1)*2 - 4;
if(a-b != tmp) return false;
int s = (y2-y1-1) * (x2-x1-1);
if(abs(s-2*b) > 1) return false;
return true;
}
void dfs(int x1, int y1, int l)
{
int x2 = x1+l, y2 = y1+l;
if(x2>n || y2>m) return;
if(check(x1, y1, x2, y2)) cnt++;
dfs(x1, y1, l+1);
}
int main()
{
int T,i,j;
scanf("%d", &T);
while(T--)
{
cnt = 0;
scanf("%d%d", &n, &m);
memset(sum,0,sizeof(sum));
for(i=1; i<=n; i++)
for(j=1; j<=m; j++)
{
scanf("%d", &mat[i][j]);
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + mat[i][j];
}
for(i=1; i<n; i++)
for(j=1; j<m; j++)
if(mat[i][j]) dfs(i, j, 1);
printf("%d\n", cnt);
}
return 0;
}
类库
C++最值常量
包含头文件:climits
|
|
signed |
unsigned |
|
|
类型 |
最小值 |
最大值 |
最大值 |
|
字符 |
SCHAR_MIN |
SCHAR_MAX |
UCHAR_MAX |
|
短整型 |
SHRT_MIN |
SHRT_MAX |
USHRT_MAX |
|
整型 |
INT_MIN |
INT_MAX |
UINT_MAX |
|
长整型 |
LONG_MIN |
LONG_MIN |
ULONG_MAX |
|
64位整型 |
LONG_LONG_MIN |
LONG_LONG_MAX |
ULONG_LONG_MAX |
类型转换
(1)string 转为其他类型
char:string.c_str();//有"\0" string.data();//没有"\0"
int:atoi(string.c_str()); atol;aoti64;
(2)数字转为char
itoa(i,num,10);
i 需要转换成字符的数字
num 转换后保存字符的变量
10 转换数字的基数(进制)
(3)char 转为string
string.assign(char);
String常用函数举例
(1)函数名 描述
size 得到字符串的大小
length 和size函数功能相同
empty 判断是否为空
c_str 取得C风格的const char* 字符串
data 取得字符串内容地址
swap 交换函数
insert 插入字符
append 追加字符
assign 和赋值操作符一样
replace 替代
find 查找
rfind 反向查找
substr 得到字串
compare 比较字符串
getline 从输入流中读入一行
(2)举例:
string s ( "Hello " ); // s="Hello "
const char *c = "Out There ";
s.append ( c ); // s="Hello Out There"
s.append ( c , 3 ); // s="Hello Out"
s.append(c,0,3);//s=”Hello Out”
s.append ( 4 , '!' ); // s1="Hello !!!!"
s.assign ( c ); // s="Out There"赋值可用
s.assign ( c , 3 ); // s="Out"
s.assign ( c ,0, 3 ); // s="Out"
s.assign ( 4 , '!' ); // s1="!!!!"
s.erase ( s.begin ( ) + 5 );//s=”Hell”
str = s.erase ( 6 , 8 ); // str 也是string
string s ( "AAAAAAAA" );
string s1p ( "BBB" );
const char* cs1p = "CCC";
a = s.replace ( 1 , 3 , s1p ); // s="ABBBAAAA"
b = s.replace ( 5 , 3 , cs1p ); // s="ABBBACCC"
s1.swap ( s2 );
C++常用头文件
#include <algorithm> //STL 通用算法
#include <bitset> //STL 位集容器
#include <cmath>
#include <complex> //复数类
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque> //STL 双端队列容器
#include <functional> //STL 定义运算函数(代替运算符)
#include <limits>
#include <list> //STL 线性列表容器
#include <map> //STL 映射容器
#include <istream> //基本输入流
#include <ostream> //基本输出流
#include <queue> //STL 队列容器
#include <set> //STL 集合容器
#include <sstream> //基于字符串的流
#include <stack> //STL 堆栈容器
#include <string> //字符串类
#include <vector> //STL 动态数组容器
模拟计算器
#include<stdio.h>
#include<string.h>
#define MAX 10005
char str[MAX];
int I,B;
__int64 fun()//处理括号
{
__int64 sum,num,b,gx;
for(sum=0,num=getnum(),b=1; 1;)
{
I++;
switch(str[I-1])
{
case '+':
sum+=num*b;
num=getnum();
b=1;
break;
case '-':
sum+=num*b;
num=getnum();
b=-1;
break;
case '*':
num*=getnum();
break;
case '/':
if((gx=getnum())==0)
{
B=-1;
gx=1;
}
num/=gx;
break;
case ')':
sum+=num*b;
return sum;
}
}
}
__int64 getnum()//提取数
{
__int64 cur;
if(str[I]>='0'&&str[I]<='9')
{
for(cur=0;str[I]>='0'
&&str[I]<='9'; I++)
cur=cur*10+str[I]-'0';
return cur;
}
else
{
I++;
switch(str[I-1])
{
case '+': return getnum();
case '-': return -1*getnum();
case '(': return fun();
}
}
}
int main()
{
__int64 l,fx;
while(gets(str))
{
l=strlen(str);
str[l]=')';
str[l+1]='\0';
I=0;
B=1;
fx=fun();
if(-1==B) printf("impossible\n");
else printf("%I64d\n",fx);
}
return 0;
}

浙公网安备 33010602011771号