ziwenの板子
ziwenの板子
组合数
ll f[300050];
ll rf[300050];
ll MOD = 998244353;
ll inv(int a) {
return qpow(a, MOD-2,MOD);
}
ll mul(int a,int b)
{
return (int)((ll)a * (ll)b % MOD);
}
ll C(int n, int k) {
return (k < 0 || k > n) ? 0 : mul(f[n], mul(rf[n-k], rf[k]));
}
void INIT()
{
f[0]=rf[0] = 1;
for (int i = 1; i <= 3e5; ++i) {
f[i] = mul(f[i-1], i);
rf[i] = mul(rf[i-1], inv(i));
}
}
int c[11][11];
void init()
{
memset(c,0,sizeof(c));
c[0][0]=c[1][0]=c[1][1]=1;
for(int i=2;i<=10;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
注意事项
写dp要注意有没有什么东西可以状态分解,比如给出一个n*n的矩阵,要找其中同色的菱形数量。那么我们可以把菱形拆成四个方向的形状,然后再用n^2跑,从而把三方转化为平方
区间(判相交,长,合并)
对于区间 [l1,r1], [l2,r2]
两个区间相交: if( l1<r2 && l2<r1)
相交长度: inter=min(r1,r2) - max(l1,l2)
合并区间长: len = r1-l1+r2-l2-inter;
堆优化 - dijkstra
void dij(int u){
priority_queue<pll,vector<pll>,greater<pll> > Q;
rep(i,1,n) dis[i] = inf;
dis[u] = 0;
Q.push(mp(dis[u],u));
while(!Q.empty()){
ll tmp = Q.top().first;
u = Q.top().second;
Q.pop();
if(dis[u]<tmp) continue;
for(auto i:G[u]){
int v = i.to;
if(dis[v] > dis[u] + i.w){
dis[v] = dis[u] + i.w;
Q.push(mp(dis[v],v));
}
}
}
}
逆元
费马
long long inv(long long a) {
return quickpow(a, MOD - 2);
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1,y=0;
return a;
}
ll ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
ll getInv(ll a,ll mod){
ll x,y;
ll d=exgcd(a,mod,x,y);
return d==1?(x%mod + mod)%mod:-1;
}
lower_bound( )和upper_bound( )
lower_bound( begin,end,num )从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num )从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
在从大到小的排序数组中,重载lower_bound()和upper_bound()
lower_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
vector<int> a;
rep(i,1,10) a.pb(i);
int index1 = lower_bound(a.begin(),a.end(),5) - a.begin();
//返回第一个大于等于5的值,所以[1 2 3 4 *5* 6 7 8 9 10]大于等于5对应的下标是4
int index2 = upper_bound(a.begin(),a.end(),5) - a.begin();
//返回第一个大于5的值,所以[1 2 3 4 5 *6* 7 8 9 10]大于5对应的下标是5
比较器
从大到小排序的set
set<int, greater<int> > s1;
set<pair<int, int>, greater<pair<int, int>>> ss;
//升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;
//降序队列,大顶堆,默认从大到小排序
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
struct ss{
int x;
ss(int xx){ x = xx;}
bool operator<(const ss& t) const{ //小于运算符,
return t.x < x; //传进来的数在左边。 这是小顶堆
}
};
priority_queue<ss> Q;
离散化
n=read();
for(int i=1;i<=n;i++)
v[i]=mp(read(),i);
sort(v+1,v+1+n);
int index=0;
for(int i=1;i<=n;i++)
{
if(v[i].X==v[i-1].X) a[v[i].Y]=index;
else a[v[i].Y]=++index;
}
离散化 - unique
// a[0~len-1] 是 从小到大排序后的数组, x是待查下标
int locate(int a[],int len,int x)
{
return lower_bound(a,a+len,x)-a;
}
int main()
{
int a[] = {1,5,8,8,4,1,2,5,4,1};
int b[] = {1,5,8,8,4,1,2,5,4,1};
sort(a,a+10);
int cnt = unique(a,a+10) - a;
for ( int i=0; i<cnt; i++ ) {
cout << a[i] << " ";
}
cout<<endl;
for ( int i=0; i<10; i++ ) {
cout<<locate(a,cnt,b[i])<<" \n"[i==10-1];
}
//输出:1 2 4 5 8
//输出:0 3 4 4 2 0 1 3 2 0
return 0;
}
循环节:
一个有n个数的环,每次循环走k步,走到每个点都有具体的权值,问在任意点出发最多走m步的情况下,一开始需要拥有多少价值才能使最终总价值不少于s。
做法: 暴力枚举所有循环节,然后单调队列找最大子段和。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define ll long long
#define rep(i,x,y) for(int i=x;i<=y;i++)
const int N = 1e4+10;
int n,m,k,cnt;
ll s,MAX,v[N];
bool vis[N]; vector<ll> g[N];
ll que[N<<1],mx[N<<1],sta[N<<1];
ll solve(const vector<ll>&vv,int count){ //单调队列找到长度最大为count的 最大子段和
int sz=vv.size();
for(int i=0;i<sz;i++)
que[i] = que[i+sz] = vv[i]; //复制一倍
sz = sz<<1;
int st = 0,ed=0;
ll res = 0;
for(int i=0;i<sz;i++){ //开始单调队列
if(i==0)
mx[i] = que[i];
else
mx[i] = mx[i-1]+que[i];
if(i<count)
res=max(res,mx[i]);
while(st<ed && sta[st]+count<i)
st++;
if(st<ed)
res = max(res,mx[i] - mx[sta[st]]);
while(st<ed && mx[i]<=mx[sta[ed-1]])
ed--;
sta[ed++] = i;
}
return res;
}
ll getRes(const vector<ll>& vv,int step){
ll mod = step % vv.size();ll kk=step / vv.size();
ll sum = 0;
for(int i=0;i<vv.size();i++)
sum+=vv[i];
ll mx1 = solve(vv,mod); //②m%sz≠0
ll mx2 = solve(vv,vv.size());//①m%sz==0
mx1 += max(0LL,sum) * kk;
mx2 += max(0LL,sum) * ((kk>2)?kk-1:0);
return max(mx1,mx2);
}
int main(){
int T;
scanf("%d",&T);
int cas=1;
while(cas<=T)
{
memset(vis,0,sizeof(vis));
scanf("%d %lld %d %d",&n,&s,&m,&k);
for(int i=0;i<n;i++)
scanf("%lld",&v[i]);
cnt = 0;
MAX = 0;
for(int i=0;i<n;i++){
g[cnt].clear(); //清零
if(!vis[i]){ //枚举起点
vis[i]=1; //起点标记
g[cnt].push_back(v[i]); //起点压入vec
for(int j=(i+k)%n;j!=i && !vis[j];j=(j+k)%n){//暴力枚举找循环节
g[cnt].push_back(v[j]);//暴力压入循环节
vis[j]=1; //标记
}
MAX = max(MAX,getRes(g[cnt],m));
cnt++;
}
}
if(MAX>=s) MAX=0;
else MAX=s-MAX;
printf("Case #%d: %lld\n", cas++, MAX);
}
}
找到相同的数距离大于x的序列
如何找到给出一堆数,你要做的就是重排后找到一个序列,使得其两个相同的数的距离要大于等于x,x就是传入的参数。比如 1 1 1 2 3 4 5 假如x=2,可以变成:1 5 4 1 3 2 1 每个1距离都为2。
做法很简单:就是先加入一个数,然后未来x个数里面都不考虑这个数,在第x+1个数里面,再看看这个数的使用次数是否为零,不为零那重新考虑这个数。 不考虑的话那就用一个优先队列去维护(谁的个数多谁贪心先放,如果不用优先队列,直接放,那么会忽略情况:比如n = 10 a=[1 1 1 1 4 4 3 3 2 2]),priority_queue里面是一个pair,第一位是个数,第二位是对应的数字,不重新考虑就直接pop,重新考虑就重新push。
bool check(int x){
rep(i,1,n) cnt[i] = a[i];
priority_queue<pii> Q;
rep(i,1,n)
if(cnt[i]) Q.push(mp(cnt[i],i));
vector<int> b;
rep(i,0,n-1){
if(i>x && cnt[b[i-x-1]]) Q.push(mp(cnt[b[i-x-1]],b[i-x-1]));
if(Q.size()==0) {
return false;
}
cnt[Q.top().Y]--;
b.pb(Q.top().Y);
Q.pop();
}
//print(b);
//wln(x);
return true;
}
树的平衡点
删除某个点后,组成的森林中最大的子树的节点个数最少 的那个点。
直接找子树大小即可。F[i]将i点删除之后最大连通块的大小,确定状态转移方程:
F[i] = max( n - tot[i], max(tot[k]))
K 是 i 的儿子
tot[i]是以i为根的子树的大小
DFS判拓扑序,有向图判环
vector<int> ord;
vector<int> used;
vector<vector<int>> g;
void dfs(int v)
{
used[v] = 1;
for(auto to:g[v]){
if(!used[to]) dfs(to);
}
ord.push_back(v);
}
void solve(){
used = vector<int>(n);
for(int i=0;i<n;i++){
if(!used[i]) dfs(i);
}
vector<int> pos(n);
reverse(ord.begin(),ord.end()); //ord模拟栈操作,所以最后需要逆序
for(int i = 0;i<n;i++){
pos[ord[i]] = i;
}
bool bad = false;
for(int i=0;i<n;i++){
for(auto j:g[i]){
if(pos[i]>pos[j]) bad=true;
}
}
if(bad) wln("-1"); //if bad==true 那么就是有环
}
Hopcroft-Karp 算法 求二分图最大匹配 复杂度:N根号E
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
const int maxn = 50000;
int nx,ny; //左右端点的数量
vector<int> v[maxn+10]; //实现邻接表;v[i]表示与左端点i相连的右边的点
int mx[maxn+10],my[maxn+10]; //mx表示左端点的归属 my表示右端点的归属
int dx[maxn+10],dy[maxn+10]; //dx表示左端点的深度 dy表示右端点的深度
bool pd[maxn+10];
bool find(int now) //从左端点u出发找增广路
{
for(int i=0;i<v[now].size();i++)
{
int id=v[now][i];
if(!pd[id]&&dy[id]==dx[now]+1)
{
pd[id]=true;
if(my[id]==0||find(my[id])) //id没归属或者id的归属可以调整
{
mx[now]=id;
my[id]=now;
return true;
}
}
}
return false;
}
int hk()
{
memset(mx,0,sizeof(mx));
memset(my,0,sizeof(my));
int ans=0;
while(true)
{
bool swt=false;
queue<int> q;
memset(dx,0,sizeof(dx));
memset(dy,0,sizeof(dy));
for(int i=1;i<=nx;i++) //没归属的都进队列
if(mx[i]==0)
q.push(i);
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<v[now].size(); i++)
{
int id=v[now][i];
if(dy[id]==0)
{
dy[id]=dx[now]+1;
if(my[id])
{
dx[my[id]]=dy[id]+1;
q.push(my[id]);
}
else
{
swt=true; //至少找到一条增广路
}
}
}
}
if(swt==false) break;
memset(pd,0,sizeof(pd));
for(int i=1;i<= nx;i++)
if(!mx[i]&&find(i))
ans++;
}
return ans;
}
int main()
{
int n,m,e;
cin>>n>>m>>e;
nx=n;ny=m;
for(int i=1;i<=e;i++)
{
int x,y;
cin>>x>>y;
v[x].push_back(y);
}
cout<<hk();
return 0;
}
两遍BFS 树的最长链
int pre[200050]; //用来记录路径,输出最长链
int get_root(int x)// 从x出发找到最远的点并返回
{
queue<pair<int,int>> Q;
Q.push(make_pair(x,1));
memset(vis,false,sizeof(vis));
vis[x]=true;
max_deep = 0;
int root=0;
pre[x] = x;
while(!Q.empty()){
int x = Q.front().first;
int deep = Q.front().second;
if(deep>max_deep){
max_deep = deep;
root = x;
}
Q.pop();
for(auto i:G[x]){
if(!vis[i]/* && !(x==delx && i==dely || x==dely && i==delx)*/){ //注释这里是把某一段给截断的意思。delxdely 是删除的那个线段,并不是删结点。
vis[i]=true;
pre[i] = x;
Q.push(make_pair(i,deep+1));
}
}
}
return root;
}
int link[200050];
int len=0;
void dfs(int x){
len = 0;
link[len++] = x;
while(pre[x]!=x){
x = pre[x];
link[len++] = x;
}
}
初始化板子
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define X first
#define Y second
#define pb push_back
#define mp make_pair
#define rush() int TT;cin>>TT;while(TT--)
#define pii pair<int,int>
#define vi vector<int>
#define vll vector<ll>
#define vpi vector<pii>
#define vpll vector<pll>
#define all(c) begin(c), end(c)
#define lb(c, x) distance((c).begin(), lower_bound(all(c), (x)))
#define ub(c, x) distance((c).begin(), upper_bound(all(c), (x)))
#define UNIQUE(x) sort(all(x)), x.erase(unique(all(x)), x.end())
#define rep0(i,n) for(int i=0;i<n;i++)
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define For(i,l,r) for(int i=l;i<=r;i++)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define rrep(i,l,r) for(int i=l;i>=r;i--)
#define wln(x) cout<<x<<"\n"
#define w(x) cout<<x;
#define wsp() cout<<" "
#define mem(a,b) memset(a,b,sizeof(a));
#define retrun return
#define endl "\n"
#define DEBUG false
#define dwln(x) if(DEBUG) cout<<"DEBUG "<<#x<<": "<<x<<"\n"
#define IOS() {std::ios::sync_with_stdio(false); /*cin.tie(NULL);*/}
void print(vector<ll > a){ //print Vii
for(int i=0;i<a.size();i++)
cout<<a[i]<<" \n"[i==a.size()-1];
}
void print(vector<int> a){ //print Vii
for(int i=0;i<a.size();i++)
cout<<a[i]<<" \n"[i==a.size()-1];
}
void print(vector<pii> a){ //print Vpii
int cnt=1;
for(auto i:a)
cout<<cnt++<<": {"<<i.X<<","<<i.Y<<"}\n";
}
void print(int a[],int l,int r){ //print a int
for(int i=l;i<=r;i++)
cout<<a[i]<<" \n"[i==r];
}
void print(ll a[],int l,int r){ //print a ll
for(int i=l;i<=r;i++)
cout<<a[i]<<" \n"[i==r];
}
vector<pii> getDiv(int a[],int l,int r) //Divide the array a in some way
{
vector<pii> ret;
rep(i,l,r)
{
int j=i;
while(j+1<=r && a[j]==a[j+1]) j++;
int len = j-i+1;
ret.pb(mp(a[i],len));
i=j;
}
return ret;
}
vector<int> getPrimeInt(int Max_prime)
{
vector<int> ret;
vector<bool> primes(Max_prime+5,true);
primes[0] = primes[1] = false;
rep(i,2,Max_prime)
{
if(primes[i])
{
ret.pb(i);
for(int j=2;i*j<=Max_prime;j++)
primes[i*j]=false;
}
}
return ret;
}
vector<bool> getPrimeBool(int Max_prime)
{
vector<int> ret;
vector<bool> primes(Max_prime+5,true);
primes[0] = primes[1] = false;
rep(i,2,Max_prime)
{
if(primes[i])
{
ret.pb(i);
for(int j=2;i*j<=Max_prime;j++)
primes[i*j]=false;
}
}
return primes;
}
vector<int> prime_sieve(const int N, const int Q = 17, const int L = 1 << 15) {
using u8 = unsigned char;
static const int rs[] = {1, 7, 11, 13, 17, 19, 23, 29};
struct P {
P(int p) : p(p) {}
int p;
int pos[8];
};
auto approx_prime_count = [](const int N) -> int { return N > 60184 ? N / (log(N) - 1.1) : max(1., N / (log(N) - 1.11)) + 1; };
const int v = sqrt(N), vv = sqrt(v);
vector<bool> isp(v + 1, true);
for(int i = 2; i <= vv; ++i)
if(isp[i]) {
for(int j = i * i; j <= v; j += i) isp[j] = false;
}
const int rsize = approx_prime_count(N + 30);
vector<int> primes = {2, 3, 5};
int psize = 3;
primes.resize(rsize);
vector<P> sprimes;
size_t pbeg = 0;
int prod = 1;
for(int p = 7; p <= v; ++p) {
if(!isp[p]) continue;
if(p <= Q) prod *= p, ++pbeg, primes[psize++] = p;
auto pp = P(p);
for(int t = 0; t < 8; ++t) {
int j = (p <= Q) ? p : p * p;
while(j % 30 != rs[t]) j += p << 1;
pp.pos[t] = j / 30;
}
sprimes.push_back(pp);
}
vector<u8> pre(prod, 0xFF);
for(size_t pi = 0; pi < pbeg; ++pi) {
auto pp = sprimes[pi];
const int p = pp.p;
for(int t = 0; t < 8; ++t) {
const u8 m = ~(1 << t);
for(int i = pp.pos[t]; i < prod; i += p) pre[i] &= m;
}
}
const int block_size = (L + prod - 1) / prod * prod;
vector<u8> block(block_size);
u8 *pblock = block.data();
const int M = (N + 29) / 30;
for(int beg = 0; beg < M; beg += block_size, pblock -= block_size) {
int end = min(M, beg + block_size);
for(int i = beg; i < end; i += prod) { copy(pre.begin(), pre.end(), pblock + i); }
if(beg == 0) pblock[0] &= 0xFE;
for(size_t pi = pbeg; pi < sprimes.size(); ++pi) {
auto &pp = sprimes[pi];
const int p = pp.p;
for(int t = 0; t < 8; ++t) {
int i = pp.pos[t];
const u8 m = ~(1 << t);
for(; i < end; i += p) pblock[i] &= m;
pp.pos[t] = i;
}
}
for(int i = beg; i < end; ++i) {
for(int m = pblock[i]; m > 0; m &= m - 1) { primes[psize++] = i * 30 + rs[__builtin_ctz(m)]; }
}
}
assert(psize <= rsize);
while(psize > 0 && primes[psize - 1] > N) --psize;
primes.resize(psize);
return primes;
}
ll qpow(ll x,ll y)
{
ll temp=x;
ll ans=1;
while(y){
if(y&1) ans=(ans*temp);
temp=(temp*temp);
y>>=1;
}
return ans;
}
ll qpow(ll x,ll y,ll MOD)
{
ll temp=x;
ll ans=1;
while(y){
if(y&1) ans=(ans*temp)%MOD;
temp=(temp*temp)%MOD;
y>>=1;
}
return ans;
}
vector<int> primes;
void INIT()
{
// primes = getPrimeInt(1e6); //获得素数vector
//vector<bool> primes = getPrimeBool(1e6); //获得素数的bool数组
/*auto P = prime_sieve(1000000);
ll d;
cin>>d;
ll ans = 1;
ll t = P[lb(P, 1 + d)];
ans *= t;
t = P[lb(P, t + d)];
ans *= t;
cout << ans << endl;*/
}
void solve()
{
}
int main()
{
IOS();
INIT();
rush()
{
solve();
}
return 0;
}
/*
ATTENTION:
1.记得初始化数组
2.0和1是很多问题解决的关键
3.别心急,坚信你不会的,别人也很难做出来
4.不要高估自己
5.不要自以为是
6.不要以为自己所训练一定会派上用场
7.想找可爱小姐姐撒娇
*/

浙公网安备 33010602011771号