ertuan模板备份
奇怪的模板增加了
数位DP
状压DP
区间DP
斜率DP
我都写了哪些东西
1 网络流最大流,费用流
2 tarjan找环与强连通与仙人掌
#include<bits/stdc++.h>
using namespace std;
const int maxn=300010;
const int MOD=998244353;
typedef long long ll;
int dfn[N], low[N], dfncnt, s[N], tp;
int scc[N], sc; // 结点 i 所在 scc 的编号
int sz[N]; // 强连通 i 的大小(点的个数)
void tarjan(int u) { // 有向图找强连通
low[u] = dfn[u] = ++dfncnt, s[++tp] = u;
for (int i = 0; i < (int)G[u].size(); i++) {
const int &v = G[u][i];
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (!scc[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
++sc;
while (s[tp] != u) scc[s[tp]] = sc, sz[sc]++, --tp;
scc[s[tp]] = sc, sz[sc]++, --tp;
}
}
//无向图仙人掌的判定和环计数:
//首先判断一个图是否是连通的,dfs一次判断点的个数即可,之后每找到一个环,
//就将环上除了根(最先遍历到的点)之外的点度数都+1,
//如果一个点度数为2,说明这个点的前向边被两个环所共有,即该图不是仙人掌图。
//无向图找环用dfs树就行 dfs树的性质:dep相同的一定没有直接相连的边
void tarjan(int u){ // 无向图仙人掌判定or找环
// siz++ siz==n 则连通
dfn[u] = ++dcnt;
for(int i = 0; i < (int)G[u].size(); i++){
int v = G[u][i];
if(v==fa[u])continue;
if(!dfn[v]){
dep[v]=dep[u]+1;
fa[v]=u;
tarjan(v);
}
}
for(int i = 0; i < (int)G[u].size(); i++){
int v = G[u][i];
if(fa[v] != u && dfn[u] < dfn[v]) {
int cnt = dep[v] - dep[u] + 1; // 环的边数
// ans = (ans * (ksm(2, cnt) - 1 + MOD) ) % MOD;
// cc -= cnt;
cal(v,u)
}
}
}
bool cal(int v,int u){ //计算入读
for(v; v != u; v = fa[v]){//这里用点来代替dfs树边
if(++cnt[v]==2)return 0;//如果一个边被两个环共用,那么不是仙人掌图
}
return 1;
}
void tarjan(int u) { // 有向图仙人掌 注意判断连通否
low[u] = dfn[u] = ++dfncnt, s[++tp] = u;
for (int i = 0; i < (int)G[u].size(); i++) {
const int &v = G[u][i];
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (!scc[v]) {
low[u] = min(low[u], dfn[v]);
if(low[v] != dfn[v]) flag = 1;//一条边属于两个或两个以上的环
}
}
if (dfn[u] == low[u]) {
++sc;
while (s[tp] != u) scc[s[tp]] = sc, sz[sc]++, --tp;
scc[s[tp]] = sc, sz[sc]++, --tp;
}
}
void tarjan(int u,int fa){ //无向图求桥AC
scc++;
low[u]=dfn[u]=++dfncnt;
for(int i=0; i<(int)G[u].size(); i++){
int v=G[u][i].to;
if(!dfn[v]){
tarjan(v, u);
low[u]=min(low[u],low[v]);
}
else if(v != fa) {
low[u]=min(low[u],dfn[v]);
}
if(edge[u][v] < 2 && low[v] > dfn[u]) { //edge判断重边 也可用存边中的亦或做
ans = min (ans, G[u][i].w);// u到v这条边就是桥
}
}
}
void tarjan(ll u,ll pre){//无向图求桥网友代码
low[u]=dfn[u]=++id;
for(int i=head[u];i!=-1;i=a[i].next){
ll v=a[i].v;
if(i==(pre^1)) continue;
if(!dfn[v]){
tarjan(v,i),low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]) bridge=min(bridge,a[i].w);
}
else low[u]=min(low[u],dfn[v]);
}
}
void tarjan(int u,int rt){ //无向图求割点AC
low[u]=dfn[u]=++dfsid;
int child=0;
for(int i=0; i<(int)G[u].size(); i++){
int v = G[u][i];
if(!dfn[v]){
child++;
tarjan(v, rt);
low[u]=min(low[u],low[v]);
if(u!=rt && low[v]>=dfn[u]){
is_cut[u] = 1;
}
}
low[u]=min(low[u],dfn[v]);
}
if(u==rt && child>1) is_cut[u] = 1;
}
3 LCA
区间DP好题
HDU - 2476 区间DP+普通DP
DP首先考虑的就是最优子结构
3.21日 区间dp大题完成,凸包使用一次,极角排序使用一次
#include<bits/stdc++.h>
using namespace std;
const int maxn=310;
const int INF=233333333;
typedef long long ll;
int n, MOD, dp[maxn][maxn], cost[maxn][maxn];
struct point{
int x, y;
point friend operator - (point a,point b) {
return {a.x-b.x,a.y-b.y};
}
}p[maxn],s[maxn];
double X(point a, point b) {
return a.x*b.y - a.y*b.x;
}
int cmp(point a,point b) {
return X(a-p[1],b-p[1])>0;
}
double multi(point p1,point p2,point p3) {
return X(p2-p1,p3-p1);
}
int main()
{
while(~scanf("%d%d",&n, &MOD)) {
for(int i=1;i<=n;i++) cin>>p[i].x>>p[i].y;
int se=1;
for(int i=2;i<=n;i++){
if(p[i].y < p[se].y || (p[i].y == p[se].y && p[i].x < p[se].x)) {
se=i;
}
}
swap(p[1], p[se]);
sort(p+2, p+1+n, cmp);
s[1]=p[1]; s[2]=p[2];
int t=2;
for(int i=3; i<=n; i++)
{
while(t>=2&&multi(s[t-1],s[t],p[i])<=0) t--;
s[++t]=p[i];
}
if(t!=n) {
printf("I can't cut.\n");
continue;
}
for(int i=1; i<=n; i++) {
for(int j=i+2; j<=n; j++) {
cost[i][j]=cost[j][i]=(abs(p[i].x+p[j].x)*abs(p[i].y+p[j].y))%MOD;
}
}
for(int len=3; len<=n; len++) {
for(int i=1; i+len-1<=n; i++) {
int j=i+len-1;
dp[i][j]=INF;
for(int k=i+1; k<j; k++) {
dp[i][j]=min(dp[i][j], dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j]);
}
}
}
printf("%d\n", dp[1][n]);
}
return 0;
}
4.8 两个进制直接转化 可以像十进制一样模拟除法,一位一位除,然后取模,余数*base加到下一位(从高到低)
马拉车
//学了马拉车,长了点记性 i 不是 1 憨憨
void Manacher(char s[], int len) {
int p=0;
ms[p++]='$'; ms[p++]='#';
for(int i=0; i<len; i++) {
ms[p++]=s[i];
ms[p++]='#';
}
ms[p]='^';
int ma=0, si=0;
for(int i=1; i<p; i++) {
mlen[i] = ma > i ? min(mlen[2*si-i], ma-i) : 1;
while(ms[i+mlen[i]]==ms[i-mlen[i]]) mlen[i]++;
if(i+mlen[i]>ma) {
ma=i+mlen[i]; si=i;
}
}
}
// ms与s都是从0开始, ms中下标为id s中对应为(id-1)/2
//回文串在原数组下标 从(i-mlen[i])/2开始,长度为mlen[i]-1;
// 注意数组要开两倍
第二次见到筛因子的算法,挺简单的,要记住
void inist() {
for(int i=1;i<=n;i++){
for(int j=1;j<=n/i;j++)
g[i*j].pb(i);
}
}
LCA当个工具吧
#include<bits/stdc++.h>
using namespace std;
const int LOG=20, maxv=100000;
vector<int> G[maxv];
int root=1;
int fa[maxv][LOG];
int dep[maxv];
void dfs(int v, int p, int d) {
fa[v][0]=p;
dep[v]=d;
for(int i=0; i<(int)G[v].size(); i++) {
if(G[v][i]!=p) dfs(G[v][i], v, d+1);
}
}
void init(int n) {
dfs(root, -1, 0);
for(int k=0; k+1<LOG; k++) {
for(int v=1; v<=n; v++) {
if(fa[v][k]<0) fa[v][k+1]=-1;
else fa[v][k+1]=fa[fa[v][k]][k];
}
}
}
int LCA(int u, int v) {
if(dep[u]>dep[v]) swap(u, v);
for(int k=LOG-1; k>=0; k--)
if(fa[v][k]!=-1 && dep[fa[v][k]]>=dep[u]) v=fa[v][k];
if(u==v) return u;
for(int k=LOG-1; k>=0; k--)
if(fa[u][k] != fa[v][k]) {
u=fa[u][k];
v=fa[v][k];
}
return fa[u][0];
}
int main() {
int n, a, b;
cin>>n;
for(int i=1; i<n; i++) {
cin>>a>>b;
G[a].push_back(b); G[b].push_back(a);
}
init(n) ;
for(int i=1; i<=n; i++) cout<<dep[i]<<' '<<fa[i][0]<<endl;
while(cin>>a>>b) {
cout<<LCA(a, b)<<endl;
}
return 0;
}
欧拉函数
ll eular(ll n) {
ll ans=n;
for(ll i=2;i*i<=n;i++) {
if(n%i==0) {
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) ans=ans/n*(n-1);
return ans;
}
折半搜索?
就是比如有30个 二选一的 方案(两个里面必须选一个(如果可以不选的话应该可以DP)),最多有230种选择,分成两半,分别查照,建立某种联系,复杂度就变成了215
例如 Gym - 102566D 先搜索前一半,将结果保存在map中,在后一半搜索,查询时候结果与map中的匹配
//
后缀数组
int s[maxn], sa[maxn], rk[maxn], t1[maxn], t2[maxn], c[maxn];
// rk[i]表示i开头的后缀排名 sa[i]表示字典序排名为i的后缀编号
void build_sa(){
//s为原数组 char或int都行
int i, *x = t1, *y = t2, m = 300;
for(i = 0 ; i < m ; i++ ) c[i] = 0;
for(i = 0 ; i < n ; i++ ) c[x[i] = s[i+1]]++; // 如果从0开始就是s[i]
for(i = 1 ; i < m ; i++ ) c[i] += c[i - 1];
for(i = n - 1 ; i >= 0 ; i-- ) sa[--c[x[i]]] = i;
for(int k = 1 ; k <= n ; k <<= 1){
int p = 0;
for(i = n - k ; i < n ; i++ ) y[p++] = i;
for(i = 0 ; i < n ; i++ ) if(sa[i] >= k) y[p++] = sa[i] - k;
for(i = 0 ; i < m ; i++ ) c[i] = 0;
for(i = 0 ; i < n ; i++ ) c[x[y[i]]]++;
for(i = 1 ; i < m ; i++ ) c[i] += c[i - 1];
for(i = n - 1 ; i >= 0 ; i-- ) sa[--c[x[y[i]]]] = y[i];
swap(x,y);
p = 1; x[sa[0]] = 0;
for(i = 1 ; i < n ; i++ )
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
if(p >= n) break;
m = p;
}
for(i = 1 ; i <= n ; i++ ) rk[sa[i-1]+1] = i;
//如果从0开始 就是for(i = 0 ; i < n ; i++ ) rk[sa[i]] = i;
return ;
}
kmp & exkmp
int Next[maxn], f[maxn], a[maxn], b[maxn];
inline void calc_next() { // 计算next数组 a为模式串 从1开始 长度为n
Next[1] = 0;
for (int i = 2, j = 0; i <= n; ++ i) {
while (j > 0 && a[i] != a[j + 1]) j = Next[j];
if (a[i] == a[j + 1]) j ++;
Next[i] = j;
}
}
inline void calc_f() { // kmp b为主串 从1开始 长度为m
for (int i = 1, j = 0; i <= m; ++ i) {
while (j > 0 && (j == n || b[i] != a[j + 1])) j = Next[j];
if (b[i] == a[j + 1]) j ++;
f[i] = j;
if (f[i] == n) {/* the first time */}
}
}
// 求 a 关于 b 的后缀的最长公共前缀, Next 记录 a关于自己每个后缀的最长公共前缀, ret 记录 a 关于 b 的后缀的最长公共前缀
void ExtendedKMP(char *a, char *b, int M, int N, int *Next, int *ret) {
int i, j, k;
for (j = 0; 1 + j < M && a[j] == a[1 + j]; ++ j);
Next[1] = j;
k = 1;
for (i = 2; i < M; ++ i) {
int Len = k + Next[k], L = Next[i - k];
if (L < Len - i) {
Next[i] = L;
} else {
for (j = max(0, Len - i); i + j < M && a[j] == a[i + j]; ++ j);
Next[i] = j;
k = i;
}
}
for (j = 0; j < N && j < M && a[j] == b[j]; ++ j);
ret[0] = j;
k = 0;
for (i = 1; i < N; ++ i) {
int Len = k + ret[k], L = Next[i - k];
if (L < Len - i) {
ret[i] = L;
} else {
for (j = max(0, Len - i); j < M && i + j < N && a[j] == b[i + j]; ++ j);
ret[i] = j;
k = i;
}
}
}
字符串哈希
const int base = 12582917; // 错误率很小的素数,可以修改
ull haha[maxn], pk[maxn];
void ppk() { // 预处理 base的k次方
pk[0] = 1;
for(int i=1; i<maxn; i++) pk[i] = pk[i-1] * base;
}
void get_hash() {
for(int i=1; i<=m; i++) {
haha[i] = haha[i-1] * base + T[i-1] - 'a' + 1; // 一定记得+1 否则会出错
}
}
//haha[l, r] = haha[r] - haha[l-1] * pk[r-l+1];
树哈希
//筛出至少n个素数,这个m一般 = n*10就差不多了
void getp(int m) {
int top = 0;
for(int i = 2; i <= m; i++) {
if(!vis[i]) {
p[++top] = i;
}
for(int j = 1; j <= top && i * p[j] <= m; j++) {
vis[i * p[j]] = 1;
if(i % p[j] == 0) break;
}
}
return;
}
//计算每个子树的哈希值
void DFS_1(int x, int f) {
siz[x] = 1;
h[x] = 1;
int sz = G[x].size();
for(int i=0; i<sz ; i++) {
int y = G[x][i];
if(y == f) continue;
DFS_1(y, x);
h[x] = ((ll) h[x] + 1ll * h[y] * p[siz[y]] %base) % base; //取模或者 用ull自然溢出,与字符串哈希相同
siz[x] += siz[y];
}
return;
}
//如果g[x]表示x作为根节点的时候,整棵树的哈希值
//那么可以根据换根算出g[x] = (g[fa] - h[x] * p[siz[x]]) * (n - siz[x]) + h[x] 需要在计算完h[x]后,再来一次dfs,公式自己推的,没验证
map pair
struct pair_hash
{
template<class T1, class T2>
std::size_t operator() (const std::pair<T1, T2>& p) const
{
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
return h1 ^ h2;
}
};
unordered_map<pair<int, bool>, int, pair_hash> Map;
找所有质因子
// O NlgNlgN
int q[maxn], b[maxn], v[maxn], c[maxn];
void init() {
for(int i=2; i<maxn; i++){
if(b[i]!=0) continue; // 改成v[i]也行
prime.push_back(i);
for(int j=i; j<maxn; j+=i){ //注意是+=i
if(b[j]==0) b[j]=i; //b[j]表示j最小的质因子,对于计算p[j]有用
c[j] = i; //c[j]表示j最大的质因子
if(j!=i) v[j]=1; //v[j]表示j不是素数
}
}
//for(int i=2; i<maxn; i++) g[c[i]].push_back(i);
q[2] = 1;
for(int i=3; i<maxn; i++) q[i] = q[i/b[i]] + 1; //p[i]表示 i 可以重复的质因子个数
}
线性筛素数
//复杂度O(N)
void getp(int m) {
int top = 0;
for(int i = 2; i <= m; i++) {
if(!vis[i]) {
p[++top] = i; // p中存的是素数
}
for(int j = 1; j <= top && i * p[j] <= m; j++) {
vis[i * p[j]] = 1; //每个合数只被最小的素数筛一次,所以 i*p[j]的最小素因子就是p[j]
if(i % p[j] == 0) break;
}
}
return;
}
快速幂 ksm
ll ksm(ll a,ll b, ll c){
ll ret = 1;
while(b){
if(b & 1) ret = (ret * a) % c;
a = (a * a) % c;
b >>= 1;
}
return ret;
}
fread快读
struct ios_in {
const int MAXN = maxn;
inline char gc() {
static char buf[maxn], *l, *r;
return (l == r) && (r = (l = buf) + fread(buf, 1, MAXN, stdin), l == r) ? EOF : *l++;
}
template <typename _Tp>
inline ios_in & operator >> (_Tp &x) {
static char ch, sgn;
for (sgn = 0, ch = gc(); !isdigit(ch); ch = gc()) {
if (!~ch) return *this;
sgn |= ch == '-';
}
for (x = 0; isdigit(ch); ch = gc())
x = (x << 1) + (x << 3) + (ch ^ '0');
sgn && (x = -x);
return *this;
}
}qin;
// qin>>a;
//带EOF的快读
a = 0;
qin>>a;
if(a==0) break;
//快速输出
int qu[66];
inline void print (ll x) {
if (x < 0) putchar ('-'), x = -x;
if (!x) putchar ('0');
int qr = 0;
while (x) qu[++ qr] = x % 10 + '0', x /= 10;
while (qr) putchar (qu[qr --]);
}
结论
cin<<scanf<<cin(false)<Qin(getchar)<<qin(fread)
printf<<cout<=cout(false)<=putchar 除了printf特别慢之外 其余相差不多
endl<<<<"\n"
关掉同步最稳妥
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout输出格式控制
double pi = 3.1415926;
cout<<fixed<<setprecision(6)<<showpoint<<pi<<"\n";
showpoint 强制输出小数,补零
setprecision (x) 保留小数点位后x位,四舍五入输出
c++手动扩栈
#pragma comment(linker, "/STACK:102400000,102400000")
// g++ 已经试验成功
//注意要写 exit(0) 不是return 0 否则编译不过
int size = 512 << 20; // 512MB
char *p = (char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p)); // Windows
__asm__("movq %0, %%rsp\n"::"r"(p)); // Linux
关于stack
最好别用傻逼stack 能自己模拟就模拟 数组是万能的
一道题目 我用stack<nb> st; 在hdu一直re 自己电脑就没事
改为数组模拟stack,ac
改为stack<int>, ac
也许有测评机的问题,但是,以后还是用数组稳妥一些
大整数
struct BigInteger
{
static const int BASE = 100000000;
static const int WIDTH = 8;
vector<LL> s;
BigInteger(long long num = 0) {*this = num;}
BigInteger operator = (long long num)
{
s.clear();
do{
s.push_back(num % BASE);
num /= BASE;
}while(num > 0);
return *this;
}
BigInteger operator = (const string & str)
{
s.clear();
int x, len = (str.size() - 1) / WIDTH + 1;
for(int i = 0 ; i < len ; i++ ){
int ed = str.size() - i * WIDTH;
int sta = max(0,ed - WIDTH);
sscanf(str.substr(sta,ed - sta).c_str(),"%d",&x);
s.push_back(x);
}
strip();
return *this;
}
bool operator < (const BigInteger & b) const
{
if(s.size() != b.s.size()) return s.size() < b.s.size();
for(int i = s.size() - 1 ; i >= 0 ; i-- )
if(s[i] != b.s[i]) return s[i] < b.s[i];
return false;
}
bool operator > (const BigInteger & b) const {return b < *this;}
bool operator <= (const BigInteger & b) const { return !(b < *this); }
bool operator == (const BigInteger & b) const {return !(b < *this) && !(*this < b); }
bool operator != (const BigInteger & b) const {return !(*this == b); }
BigInteger operator * (const BigInteger & b) const
{
///cout << *this << " * " << b << endl;
///printf("*1\n");
BigInteger c;
c.s.resize(s.size() + b.s.size() + 1,0);
for(int i = 0 ; i < (int)s.size() ; i++ )
for(int j = 0 ; j < (int)b.s.size() ; j++ )
c.s[i + j] += s[i] * b.s[j];
for(int i = 0 ; i < (int)s.size() + (int)b.s.size() ; i++ ) if(c.s[i] > BASE)
{
c.s[i + 1] += c.s[i] / BASE;
c.s[i] %= BASE;
}
if((int)c.s.size() > (n+7)/8) c.s.resize((n+7)/8);
c.strip();
///printf("*2\n");
return c;
}
int mult(const BigInteger & d,const BigInteger & b) const
{
int L = 0,R = BASE - 1;
while(L < R)
{
int m = (L + R) / 2;
if((2 * m) != L + R) ++m;
BigInteger p = b * m;
if(p == d) return m;
else if(p < d) L = m;
else R = m - 1;
}
return L;
}
BigInteger operator / (const BigInteger & b)
{
///printf("\1\n");
if(b == 0) return -1;
BigInteger c,d; c.s.resize(s.size());
for(int i = s.size() - 1 ; i >= 0 ; i-- )
{
d.s.insert(d.s.begin(),s[i]);
d.strip();
int cnt = mult(d,b);
d = d - b * cnt;
c.s[i] = cnt;
}
///printf("\2\n");
c.strip(); return c;
}
BigInteger operator - (const BigInteger & b)
{
BigInteger c; c.s.resize(s.size(),0);
for(int i = 0 ; i < (int)c.s.size() ; i++ )
c.s[i] = s[i] - (i < (int)b.s.size() ? b.s[i] : 0);
int i = c.s.size() - 1;
for( ; i >= 0 ; i-- ) if(c.s[i]) break;
for(i-- ; i >= 0 ; i-- ) if(c.s[i] < 0){
int j = i + 1,k = 0;
while(j < (int)c.s.size() && !c.s[j]) {j++; k++;}
c.s[j]--; c.s[i] += BASE;
for( ; k > 0 ; k-- ) c.s[i + k] = BASE - 1;
}
c.strip(); return c;
}
void strip()
{
while(!s.empty() && !s.back()) s.pop_back();
if(s.empty()) s.push_back(0);
return ;
}
BigInteger operator + (const BigInteger & b) const{
BigInteger c; c.s.clear();
for(int i = 0 ,g = 0 ; ; i++ ){
if(g == 0 && i >= (int)s.size() && i >= (int)b.s.size()) break;
int x = g;
if(i < (int)s.size()) x += s[i];
if(i < (int)b.s.size()) x += b.s[i];
c.s.push_back(x % BASE);
g = x / BASE;
}
return c;
}
};
BigInteger jb;
istream& operator >> (istream &in,BigInteger & x)
{
string s;
if(!(in >> s)) return in;
x = s;
return in;
}
ostream& operator << (ostream &out ,const BigInteger & x)
{
int cnt = 0;
out << x.s.back();
cnt += 8;
for(int i = x.s.size() - 2 ; i >= 0 ; i-- )
{
char buf[20];
sprintf(buf,"%08lld",x.s[i]);
for(int j = 0 ; j < (int)strlen(buf) ; j++ ) {
cnt++;
if(cnt >= n) break;
out << buf[j];
}
}
return out;
}
//可以直接乘除加减,输入输出,直接cin>> cout<<即可
PBDS
gp_hash_table的使用,比unordered_map更优秀,用法相同
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
const int RANDOM = chrono::high_resolution_clock::now().time_since_epoch().count();
struct chash {
int operator()(int x) const { return x ^ RANDOM; }
};
gp_hash_table<int, int, chash> table; // 加上chash为了防着被hack,可以去掉
//如果用pair怎么办
typedef pair<int, int> pii;
struct chash {
int operator()(pii x) const { return x.first* 31 + x.second; }
};
gp_hash_table<pii, int, chash> table;
Dinic最优写法
//玄学优化技巧,如果跑二分图,源点S连点数少的那一边,跑的飞快(也有可能效果相反),总之建图不同,结果不同,灵活转换
struct Dinic {
struct edge {
int nt, to; ll cap;
};
edge E[maxm];
int head[maxn], q[maxm];
int level[maxn], cur[maxn];
int ccnt, n, s, t;
void init(int _n, int _s, int _t) {
n = _n; s = _s; t = _t;
ccnt = 0;
for(int i=0; i<=n; i++) head[i] = -1;
}
void add_edge(int from, int to, ll cap) {
E[ccnt]=edge{head[from], to, cap};
head[from]=ccnt++;
E[ccnt]=edge{head[to], from, 0};
head[to]=ccnt++;
}
void bfs() {
for(int i=0; i<=n; i++) level[i]=-1;
level[s]=0;
int fr=0, re=0;
q[re++]=s;
while(fr!=re) {
int v=q[fr++];
if(fr==maxm) fr=0;
for(int i=head[v]; ~i; i=E[i].nt) {
edge e=E[i];
if(e.cap>0 && level[e.to]<0) {
level[e.to]=level[v]+1;
q[re++]=e.to;
if(re==maxm) re=0;
if(e.to==t) return; // 优化
}
}
}
}
ll dfs(int v, ll f) {
if(v==t) return f;
ll sum=0;
for(int &i=cur[v]; ~i; i=E[i].nt) {
edge &e=E[i];
if(e.cap>0 && level[v]<level[e.to]) {
ll d=dfs(e.to, min(f, e.cap));
if(d>0) {
e.cap-=d;
E[i^1].cap+=d;
f-=d;
sum+=d;
if(f<=0) break; //this is YouHua too
}
}
}
return sum;
}
ll solve() {
ll flow=0;
while(1) {
bfs();
if(level[t]<0) return flow;
for(int i=0; i<=n; i++) cur[i]=head[i];
ll f=0;
while((f=dfs(s, INF))>0) {
flow+=f;
}
}
return flow;
}
};
ISAP模板
//找时间转化成前向星
struct Edge {
int from, to, cap, flow;
Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};
bool operator < (const Edge& a, const Edge& b) {
return a.from < b.from || (a.from == b.from && a.to < b.to);
}
struct ISAP {
int n, m, s, t;
vector<Edge> edges;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
int p[maxn];
int num[maxn];
void AddEdge(int from, int to, int cap) {
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool BFS() {
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(t);
vis[t] = 1;
d[t] = 0;
while (!Q.empty()) {
int x = Q.front();
Q.pop();
for (int i = 0; i < (int)G[x].size(); i++) {
Edge& e = edges[G[x][i] ^ 1];
if (!vis[e.from] && e.cap > e.flow) {
vis[e.from] = 1;
d[e.from] = d[x] + 1;
Q.push(e.from);
}
}
}
return vis[s];
}
void init(int n) {
this->n = n;
for (int i = 0; i < n; i++)
G[i].clear();
edges.clear();
}
int Augment() {
int x = t, a = INF;
while (x != s) {
Edge& e = edges[p[x]];
a = min(a, e.cap - e.flow);
x = edges[p[x]].from;
}
x = t;
while (x != s) {
edges[p[x]].flow += a;
edges[p[x] ^ 1].flow -= a;
x = edges[p[x]].from;
}
return a;
}
int Maxflow(int s, int t) {
this->s = s;
this->t = t;
int flow = 0;
BFS();
memset(num, 0, sizeof(num));
for (int i = 0; i < n; i++)
num[d[i]]++;
int x = s;
memset(cur, 0, sizeof(cur));
while (d[s] < n) {
if (x == t) {
flow += Augment();
x = s;
}
int ok = 0;
for (int i = cur[x]; i < (int)G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if (e.cap > e.flow && d[x] == d[e.to] + 1) {
ok = 1;
p[e.to] = G[x][i];
cur[x] = i;
x = e.to;
break;
}
}
if (!ok) {
int m = n - 1;
for (int i = 0; i < (int)G[x].size(); i++) {
Edge& e = edges[G[x][i]];
if (e.cap > e.flow)
m = min(m, d[e.to]);
}
if (--num[d[x]] == 0)
break;
num[d[x] = m + 1]++;
cur[x] = 0;
if (x != s)
x = edges[p[x]].from;
}
}
return flow;
}
};
ISAP tp;
MCMF模板
//vector的好处是动态内存,多次使用的时候不会爆内存,如果只是使用一次,用前向星较快,G[i].size()也比较费时,尽量只用一次
//函数中用全局变量要小心,尤其是递归改变了这个全局变量,你却没发现
struct MCMF {
struct nb {
int from, to; ll cap, cost;
};
int s, t, n, m;
vector<nb> E;
vector<int> G[maxn];
ll d[maxn], inq[maxn], a[maxn], p[maxn];
void init(int _n, int _s, int _t) {
n = _n; s = _s; t = _t;
E.clear();
for(int i=0; i<=n; i++) G[i].clear();
m=0;
}
void add_edge(int from, int to, int cap, int cost) {
E.push_back(nb{from, to, cap, cost});
E.push_back(nb{to, from, 0, -cost});
G[from].push_back(m++);
G[to].push_back(m++);
}
bool SPFA() {
for(int i=0; i<=n; i++) d[i]=INF, inq[i]=0;
d[s]=0; inq[s]=1; p[s]=0; a[s]=INF;
queue<int> q;
q.push(s);
while(!q.empty()) {
int u=q.front(); q.pop();
inq[u]=0;
int sz = G[u].size();
for(int i=0; i<sz; i++) {
nb &e=E[G[u][i]];
if(e.cap>0 && d[e.to]>d[u]+e.cost) {
d[e.to]=d[u]+e.cost;
p[e.to]=G[u][i];
a[e.to]=min(a[u], e.cap);
if(!inq[e.to]) {
q.push(e.to); inq[e.to]=1;
}
}
}
}
if(d[t]==INF) return 0;
return 1;
}
ll solve(ll & cost) {
ll flow=0;
while(SPFA()) {
flow+=a[t];
cost+=d[t]*a[t];
for(int u=t; u!=s; u=E[p[u]].from) {
E[p[u]].cap-=a[t];
E[p[u]^1].cap+=a[t];
}
}
return flow;
}
} D;
二分图性质
1.二分图最小点覆盖 = 最大匹配
求最小点覆盖:从右边所有没有匹配过的点出发,按照增广路的“交替出现”的要求DFS。最终右边没有访问过的点和左边访问过的点组成最小点覆盖。
2.最小边覆盖 = 点数 - 最大匹配
匹配的边选上,落单的点随便连一条边选上
3.最大独立集 = 点数 - 最大匹配
先把所有的点放进集合,然后删去最少的点和与之相关联的边,使得全部边都被删完,这就是最小点覆盖。所以有:最大独立集=点数-最小点覆盖
二分图最大匹配模板
int dfs(int v) {
vis[v] = dfn;
for(int i=0; i<(int)G[v].size(); i++) {
int u=G[v][i], w=ml[u];
if(w<0 || (vis[w]!=dfn && dfs(w))) {
ml[v]=u;
ml[u]=v;
return 1;
}
}
return 0;
}
int max_match() {
int res=0;
dfn = 0;
memset(ml, -1, sizeof(ml));
memset(mr, -1, sizeof(mr));
memset(vis, 0, sizeof(vis));
for(int i=1; i<=cnt; i++) {
if(ml[i]<0) {
dfn++;
if(dfs(i)) res++;
}
}
return res;
}
二分图带权最大匹配模板
DAG转化二分图最小点/边覆盖模板
1.DAG最小不相交路径覆盖
算法:把原图的每个点V拆成Vx和Vy两个点,如果有一条有向边A->B,那么就加边Ax−>By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数。
证明:一开始每个点都是独立的为一条路径,总共有n条不相交路径。我们每次在二分图里找一条匹配边就相当于把两条路径合成了一条路径,也就相当于路径数减少了1。所以找到了几条匹配边,路径数就减少了多少。所以有最小路径覆盖=原图的结点数-新图的最大匹配数。
2.DAG最小可相交路径覆盖
算法:先用floyd求出原图的传递闭包,即如果a到b有路径,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。
证明:为了连通两个点,某条路径可能经过其它路径的中间点。比如1->3->4,2->4->5。但是如果两个点a和b是连通的,只不过中间需要经过其它的点,那么可以在这两个点之间加边,那么a就可以直达b,不必经过中点的,那么就转化成了最小不相交路径覆盖。
3.Dilworth定理:DAG的最大独立集 = DAG最少不相交路径覆盖
DAG的独立集我也不懂啥意思,可能就是点集V中的点不能到达(单向到达)
不是DAG怎么办,强连通分量缩点
手动开O3
#pragma GCC optimize(3)
Dijkstra模板
int vis[maxn], dis[maxn];
priority_queue< pair<int, int> > q;
void add(int u, int v, ll w) {
E.push_back(nb{u, v, w});
E.push_back(nb{v, u, w});
G[u].push_back(E.size()-2);
G[v].push_back(E.size()-1);
}
void dij(int s) {
for(int i=1; i<=n; i++) dis[i] = INF, vis[i] = 0;
dis[s] = 0;
q.push(make_pair(0, s));
while(!q.empty()) {
int u = q.top().second;
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i=0; i<(int)G[u].size(); i++) {
nb e = E[G[u][i]];
if(dis[e.v] > dis[u] + e.w) {
dis[e.v] = dis[u] + e.w;
q.push(make_pair(-dis[e.v], e.v));
}
}
}
}
一些不会的函数
shuffle(a + 1, a + n + 1, mt19937(time(NULL))); //随机重排
组合数相关
一个长为n的序列,其中ai出现了bi次bi的和为n,可能的序列为n!/(b1! *b2! * ... * bm!)
啦啦啦
线段树
struct SegTree {
int sum[maxn * 4], lazy[maxn * 4];
void build(int o, int l, int r) {
sum[o] = lazy[o] = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
sum[o] = sum[lc] + sum[rc];
}
void pushdown(int o, int l, int r) {
if(lazy[o]) {
int mid = (l + r) >> 1;
lazy[lc] += lazy[o];
sum[lc] += (mid - l + 1) * lazy[o];
lazy[rc] += lazy[o];
sum[rc] += (r - mid) * lazy[o];
lazy[o] = 0;
}
}
int query2(int o, int l, int r, int ql, int qr) { // sum
if (l > qr || r < ql) return 0;
if (ql <= l && r <= qr) return sum[o];
pushdown(o, l, r);
int mid = (l + r) >> 1;
return query2(lc, l, mid, ql, qr) + query2(rc, mid + 1, r, ql, qr);
}
void update(int o, int l, int r, int ql, int qr, int t) {
if (ql <= l && r <= qr) {
sum[o] += (r - l + 1) * t;
lazy[o] += t;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1;
if (qr <= mid) update(lc, l, mid, ql, qr, t);
else if(ql > mid) update(rc, mid + 1, r, ql, qr, t);
else update(lc, l, mid, ql, qr, t), update(rc, mid + 1, r, ql, qr, t);
sum[o] = sum[lc] + sum[rc];
}
} st;
树链剖分
void dfs1(int u) {
son[u] = -1;
siz[u] = 1;
for(int i=0; i<(int)G[u].size(); i++){
int v = G[u][i];
if(v!=fa[u]) {
dep[v] = dep[u] + 1;
fa[v] = u;
dfs1(v);
siz[u] += siz[v];
if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
}
}
}
void dfs2(int u, int t) {
top[u] = t;
dfn[u] = ++dcnt;
rnk[dcnt] = u;
if (son[u] == -1) return;
dfs2(son[u], t);
for (int i=0; i<(int)G[u].size(); i++) {
int v = G[u][i];
if (v != son[u] && v != fa[u]) dfs2(v, v);
}
}
主席树第k小
const int N = 100010, LOG = 15;
int n, q, m, cnt = 0;
int a[N], b[N], T[N];
int sum[N*LOG], L[N*LOG], R[N*LOG];
inline int build(int l, int r)
{
int rt = ++ cnt;
sum[rt] = 0;
if (l < r){
L[rt] = build(l, mid);
R[rt] = build(mid+1, r);
}
return rt;
}
inline int update(int pre, int l, int r, int x)
{
int rt = ++ cnt;
L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre]+1;
if (l < r){
if (x <= mid) L[rt] = update(L[pre], l, mid, x);
else R[rt] = update(R[pre], mid+1, r, x);
}
return rt;
}
inline int query(int u, int v, int l, int r, int k)
{
if (l >= r) return l;
int x = sum[L[v]] - sum[L[u]];
if (x >= k) return query(L[u], L[v], l, mid, k);
else return query(R[u], R[v], mid+1, r, k-x);
}
// 用法: 读入a[i] , b[i] = a[i]
//处理
sort(b+1, b+1+n);
m = unique(b+1, b+1+n)-b-1;
T[0] = build(1, m);
for (int i = 1; i <= n; i ++){
int t = lower_bound(b+1, b+1+m, a[i])-b;
T[i] = update(T[i-1], 1, m, t);
}
//查询
for(int i=1; i+l-1<=n; i++) {
int x = i, y = i+l-1;
// x 是左端点, y是右端点 z = l-k+1 就是区间第z小,区间第k大
int t = query(T[x-1], T[y], 1, m, l-k+1);
ans1 += b[t];
}
生成树个数
// O(n^3 * lgm)
LL K[N][N];
ll ans[35];
LL gauss(int n){//求矩阵K的n-1阶顺序主子式
LL res=1;
for(int i=1;i<=n-1;i++){//枚举主对角线上第i个元素
for(int j=i+1;j<=n-1;j++){//枚举剩下的行
while(K[j][i]){//辗转相除
int t=K[i][i]/K[j][i];
for(int k=i;k<=n-1;k++)//转为倒三角
K[i][k]=(K[i][k]-t*K[j][k]+MOD)%MOD;
swap(K[i],K[j]);//交换i、j两行
res=-res;//取负
}
}
res=(res*K[i][i])%MOD;
}
return (res+MOD)%MOD;
}
//用法 输入u v 无向图
K[u][u]++;
K[v][v]++;
K[u][v]--;
K[v][u]--;
矩阵快速幂
struct Matrix {
long long a[3][3];
Matrix() {
memset(a, 0, sizeof(a));
}
void init(){
a[1][1] = a[1][2] = 1;
a[2][1] = -1;
a[2][2] = 0;
}
Matrix operator*(const Matrix b) {
Matrix res;
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
for (int u = 1; u <= 2; u++)
res.a[i][j] = (res.a[i][j] + a[i][u]*b.a[u][j])%mod;
return res;
}
};
long long q_pow(long long n){
Matrix ans,base;
ans.init();
base.init();
while(n > 0){
if(n&1) ans =ans *base;
base = base *base;
n >>= 1;
}
return ans.a[1][1];
}
[Fn, Fn−1] = [Fn−1, Fn−2] * [1, 1; 1, 0]
some 模板
树状数组
// 一维
void add(int x, int s) {
for(int i=x; i<=n; i+=(i&-i)) {
sum[i]+=s;
}
}
int query(int x) {
int res=0;
for(int i=x; i>=1; i-=(i&-i)) {
res+=sum[i];
}
return res;
}
//二维
void add(int x, int y, int s) {
for(int i=x; i<=m; i+=(i&-i)) {
for(int j=y; j<=n; j+=(j&-j)) {
sum[i][j]+=s;
}
}
}
int query(int x, int y) {
int res=0;
for(int i=x; i>=1; i-=(i&-i)) {
for(int j=y; j>=1; j-=(j&-j)) {
res+=sum[i][j];
}
}
return res;
}
KMP
inline void calc_next() { // 计算next数组 a为模式串 从0开始 长度n
next[0] = 0;
for (int i = 1, j = -1; i < n; ++ i) {
while (j >= 0 && a[i] != a[j + 1]) j = next[j];
if (a[i] == a[j + 1]) j ++;
next[i] = j;
}
}
inline void kmp() { // kmp b为主串 从0开始 长度为m
for (int i = 0, j = -1; i < m; ++ i) {
while (j >= 0 && (j == n-1 || b[i] != a[j + 1])) j = next[j];
if (b[i] == a[j + 1]) j ++;
f[i] = j;
if (f[i] == n-1) {
/* the first time */
// b[i-n+1, i] == a[0, n-1]
j=-1; //初始化j
}
}
}
exkmp
// 求a与b的每一个后缀的最长公共前缀,Next记录a与自己每个后缀的最长公共前缀, ret 记录 a 与 b 的后缀的最长公共前缀的长度
void exkmp() {
// a: 模板串,长度M; b是主串 长度N; 都是从零开始
//这个代码已经很简洁了 😁
int i, j, k;
for (j = 0; 1 + j < M && a[j] == a[1 + j]; ++ j);// 注意这里有分号
Next[1] = j;
k = 1;
for (i = 2; i < M; ++ i) {
int Len = k + Next[k], L = Next[i - k];
if (L < Len - i) {
Next[i] = L;
} else {
for (j = max(0, Len - i); i + j < M && a[j] == a[i + j]; ++ j);// 还有这
Next[i] = j;
k = i;
}
}
for (j = 0; j < N && j < M && a[j] == b[j]; ++ j);// me too
ret[0] = j;
k = 0;
for (i = 1; i < N; ++ i) {
int Len = k + ret[k], L = Next[i - k];
if (L < Len - i) {
ret[i] = L;
} else {
for (j = max(0, Len - i); j < M && i + j < N && a[j] == b[i + j]; ++ j);//too
ret[i] = j;
k = i;
}
}
}
SPFA
struct edge {
int from, to; ll dist;
edge(int from, int to, ll dist):from(from), to(to), dist(dist){}
};
vector<edge> E; // 存边
vector<int> G[maxn];
int inq[maxn]; // 保存点是否在队列中
ll D[maxn]; // 最短路数组
void add_edge(int from, int to, ll dist) {
E.push_back(edge(from, to, dist));
G[from].push_back(E.size()-1);
/* 无向图的话 要加上
E.push_back(edge(to, from, dist);
G[to].push_back(E.size()-1); */
}
void SPFA(int s) {
for(int i=1; i<=n; i++) D[i]=INF;
memset(inq, 0, sizeof(inq));
queue<int> q; q.push(s);
inq[s]=1; D[s]=0;
while(!q.empty()) {
int v=q.front(); q.pop(); inq[v]=0;
for(int i=0; i<(int)G[v].size(); i++) {
edge e=E[G[v][i]];
if(D[e.to] > D[v]+e.dist) {
D[e.to]=D[v]+e.dist;
if(!inq[e.to]) {
q.push(e.to); inq[e.to]=1;
}
}
}
}
}
线段树
//支持区间修改(区间中每个点都加num),区间查询(最大值,最小值,区间和..) 每个操作复杂度log N
#include<bits/stdc++.h>
using namespace std;
const int maxn=60000+10;
const int INF=0x3f3f3f3f;
struct nb{
int L, R, v, lazy; // 左边界,右边界,值,遗传标记
} node[maxn*4]; // 数组要开四倍大小 因为是树形结构
int n, m, ans, s;
void Build(int o, int L, int R) // 建树
{
node[o].L=L; node[o].R=R;
if(L==R) return;
Build(o*2, L, (L+R)/2);
Build(o*2+1, (L+R)/2+1, R);
}
void pushdown(int o) //遗传
{
if(node[o].lazy) {
int a=o*2, b=o*2+1, c=node[o].lazy;
node[a].v+=c;
node[a].lazy+=c;
node[b].v+=c;
node[b].lazy+=c;
}
node[o].lazy=0;
}
void Update(int o, int L, int R, int n) // 区间[L, R] 增加 n
{
if(node[o].L>R || node[o].R<L) return;
if(node[o].L>=L &&node[o].R<=R) {
node[o].v+=n;
node[o].lazy+=n;
}
else {
pushdown(o);
Update(o*2, L, R, n);
Update(o*2+1, L, R, n);
node[o].v=max(node[o*2].v, node[o*2+1].v);
}
}
void Query(int o, int L, int R) // 查询 o为编号 L,R为边界
{
if(node[o].L>R || node[o].R<L) return;
if(node[o].L>=L && node[o].R<=R) {
ans=max(ans, node[o].v); return;
}
else {
pushdown(o);
Query(o*2, L, R);
Query(o*2+1, L, R);
}
}
int main()
{
//freopen("railway.in", "r", stdin);
//freopen("railway.out", "w", stdout); 文件操作..用不到
scanf("%d%d%d", &n, &s, &m);
Build(1, 1, n);
for(int i=1; i<=m; i++) {
int o, d, x;
scanf("%d%d%d", &o, &d, &x);
ans=0; // ans为全局变量需要初始化 本题求最大值故初始化为0
Query(1, o, d-1);
if(s-ans>=x) {
printf("YES\n");
Update(1, o, d-1, x);
}
else printf("NO\n");
}
return 0;
}
RMQ
// ST算法 求区间最值 预处理NlogN 查询O(1) 查询次数较多时RMQ效率优于线段树 但是不支持修改操作
vector<int> A; // 存数据
int d[maxn][30]; // d[i][j]表示从i开始长度为2^j的一段元素中的最小值 所以第二维开30就够了
void init() { // nlogn的预处理
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]=min(d[i][j-1], d[i+(1<<(j-1))][j-1]);
}
}
}
int RMQ(int L, int R) { // 查询[L, R] 最小值 从0开始 复杂度O(1);
int k=0;
while((1<<(k+1)) <= R-L+1) k++;
return min(d[L][k], d[R-(1<<k)+1][k]);
}
int main() {
for(int i=1; i<=10; i++) cin>>n, A.push_back(n);
init(); // 读入完成后 开始预处理
while(cin>>n>>m) {
cout<<RMQ(n, m)<<endl;
}
}
网络流
// Dinic算法求最大流(最小割) 理论复杂度O(N^2*M) n节点数 m边数 但是实际运行很快 几百几千的数据大胆跑
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=500;
const ll INF=23333333333333;
int n, m, k, f, d;
struct edge {
int from, to; ll cap;
edge(int from, int to, ll cap):from(from), to(to), cap(cap){}
};
vector<edge> E;
vector<int> G[maxn];
int level[maxn], cur[maxn], inq[maxn];
ll S[maxn];
void add_edge(int from, int to, ll cap) {
E.push_back(edge(from, to, cap));
E.push_back(edge(to, from, 0));
G[from].push_back(E.size()-2);
G[to].push_back(E.size()-1);
}
void bfs(int s) {
memset(level, -1, sizeof(level));
level[s]=0;
queue<int> q;
q.push(s);
while(!q.empty()) {
int v=q.front(); q.pop();
for(int i=0; i<(int)G[v].size(); i++) {
edge e=E[G[v][i]];
if(e.cap>0 && level[e.to]<0) {
level[e.to]=level[v]+1;
q.push(e.to);
}
}
}
}
ll dfs(int v, int t, ll f) {
if(v==t) return f;
for(int &i=cur[v]; i<(int)G[v].size(); i++) {
edge &e=E[G[v][i]];
if(e.cap>0 && level[v]<level[e.to]) {
ll d=dfs(e.to, t, min(f, e.cap));
if(d>0) {
e.cap-=d;
E[G[v][i]^1].cap+=d;
return d;
}
}
}
return 0;
}
ll dinic(int s, int t) {
ll flow=0;
while(1) {
bfs(s);
if(level[t]<0) return flow;
memset(cur, 0, sizeof(cur));
ll f=0;
while((f=dfs(s, t, INF))>0) {
flow+=f;
}
}
return flow;
}
int main() {
E.clear();
For(int i=1; i<=n; i++) G[i].clear();
ll ans=dinic(0, 433);
cout<<ans<<endl;
return 0;
}
贪心
区间选点问题
//问题描述:数轴上有n个闭区间 [ai,bi][ai,bi],要求选取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)。
解法:按照左端点排序(左同则右端点递增),选择把点放在右端点上,从前往后遍历,超出右端点范围则ans++,遇到右端点比当前右端点小的,更新右端点为小的。
题型
1. 裸题:套模板即可;
2. 需要转化后套模板 eg: https://vjudge.net/problem/POJ-1328 (最少雷达覆盖所有点,转化为x轴上区间选点)
区间覆盖问题
//问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),求最少使用多少条线段可以将整个区间完全覆盖。
解法:按照左端点排序(左同则右端点递增),从前往后遍历,设置一个变量表示已覆盖到的区间右端点,在剩下的线段中找出所有左端点小于等于当前已覆盖到的区间右端点的线段,选择右端点最大并且大于当前已覆盖到的区间右端点,重复以上操作直至覆盖整个区间;
例题:http://www.51mxd.cn/problem.php-pid=12.htm (将喷水装置转化为x轴上的区间,求最小区间覆盖即可)
不相交区间问题
//问题描述:数轴上有n个区间[ai,bi][ai,bi],要求选择尽量多个区间,使得这些区间两两没有公共点。
解法:按照右端点排序(右同则左端点递增),从前往后遍历,如果它与当前已选的所有区间都没有相交,则选择该区间,否则不选。左端点大小不影响答案。
例题:http://www.51mxd.cn/problem.php-pid=14.htm(会场安排问题)
排序问题
//就是那种有两个参数 比如一个是时间 一个是受到伤害量 让你选择一种顺序使结果最优的
//看上去并不能按照一个变量无脑贪心的 那就写到结构体里 operator 按照神奇规则排序 你懂得
O(n)找第K大
int find_k(int l, int r, int k) { // [1, n] 找第k大的数
if(l==r) return a[l];
int temp=a[l], s=l, t=r;
while(s<t) {
while(s<t && a[t]>=temp) t--;
swap(a[s], a[t]);
while(s<t && a[s]<temp) s++;
swap(a[s], a[t]);
}
if(s-l+1==k) return a[s];
else if(s-l+1>k) return find_k(1, s, k);
else return find_k(s+1, r, k-(s-l+1));
}
// 调用 cout<<find_k(1, n, k)<<endl
树的直径
//定义: 一棵树中距离最远的两个点的距离叫做树的直径
void dp(int x){ //树形dp求直径 设d[x]表示从节点x出发,往以x为根的子树走,能够到达的最远距离。
v[x]=1;
for(int i=0; i<(int)G[x].size(); i++){
edge e=E[G[x][i]];
int y=e.to;
if(v[y])continue;
dp(y);
ans=max(ans,d[x]+d[y]+e.dist);
d[x]=max(d[x],d[y]+e.dist);
}
}
膜2019?
void dfs(int u, int f) {
int len = g[u].size();
for(int i = 0; i < len; i++) {
int v = g[u][i].v;
int w = g[u][i].w;
if(v == f) continue;
cntdown[v][w%2019]++;
if(w%2019 == 0) ans++;
for(int j = 0; j <= 2018; j++) {
int cnt = cntdown[u][j] + cntup[u][j];
if(cnt == 0) continue;
cntdown[v][(j+w)%2019] += cnt;
if((j + w) % 2019 == 0) ans += cnt;
}
dfs(v, u);
cntup[u][w%2019]++;
for(int j = 0; j <= 2018; j++) {
int cnt = cntup[v][j];
if(cnt == 0) continue;
cntup[u][(j+w)%2019] += cnt;
}
}
}
一个数学规律 a^b = a+b- 2*(a&b)

浙公网安备 33010602011771号