丐版 OI 技巧 / 杂项部分总结 + 作者学习笔记

合作:

SKK 部分【前面忘了】,SKK,我【中间忘了】的信仰,我【中间忘了】的希望https://www.cnblogs.com/S-Keep-Kiding/p/19267094

Wy_x 部分https://www.cnblogs.com/Wy-x/p/19265940


我写的的学习笔记部分:

1. 斜率优化 学习笔记

2. wqs 二分(凸完全单调性)

3. 树上依赖性背包 学习笔记 | P6326 Shopping 题解

4. 动态 dp 学习笔记(树剖版)

5. 辗转相减法求高斯消元(Martix Tree)

整体二分学习笔记

格路计数的一类(降维?)技巧

决策单调性 dp 的分治解法(整体二分解法)


Tricks:收录不常见模板题,经典好题/思维题/trick 题(也有一些神秘题)。CF/AT 后面可能会补。

1. 可持久化区间修改区间查询线段树:

SP11470 TTM - To the moon

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N=1e5+5;
int n,m;
int a[N],f[N];
// dt: ;子树里的 add 总和
// lazy : 该区间懒标记
struct Tree{
	int dt,lazy;
	int lp,rp;
}t[N*500];//n log_n log_(n log_n)
int tot;
int root[N];

int add(int l,int r,int sl,int sr,int k,int lastp,int &nwp)
{
	if(!nwp) nwp=++tot;
	if(sl<=l&&r<=sr)
	{
		t[nwp]={t[lastp].dt+k*(r-l+1),t[lastp].lazy+k,t[lastp].lp,t[lastp].rp};
		
// cerr<<l<<" "<<r<<" "<<sl<<" "<<sr<<" k="<<k<<" len="<<r-l+1<<" ";
// cerr<<"t[nwp].lazy="<<t[nwp].lazy<<" t[nwp].dt="<<t[nwp].dt<<" ";
// cerr<<"t[lastp].lazy="<<t[lastp].lazy<<" t[lastp].dt="<<t[lastp].dt<<"\n";
		return k*(r-l+1);
	}
	int mid=(l+r)>>1,dt=0;
	if(sl<=mid) dt=add(l,mid,sl,sr,k,t[lastp].lp,t[nwp].lp);
	else t[nwp].lp=t[lastp].lp;
	if(sr>mid) dt+=add(mid+1,r,sl,sr,k,t[lastp].rp,t[nwp].rp);
	else t[nwp].rp=t[lastp].rp;

	t[nwp].lazy=t[lastp].lazy;
	t[nwp].dt=t[lastp].dt+dt;
//	cerr<<l<<" "<<r<<" "<<sl<<" "<<sr<<" ";
//	cerr<<"t[nwp].lazy="<<t[nwp].lazy<<" t[nwp].dt="<<t[nwp].dt<<"\n";
	return dt;
}

// pair<int,int> operator+(const pair<int,int> &x,const pair<int,int> &y)
// {
// 	pair<int,int> ans=make_pair(0,0);
// 	ans.first=x.first+y.first;
// 	ans.second=x.second+y.second;
// 	return ans;
// }

int find(int l,int r,int sl,int sr,int nw_lazy,int p)
{
	if(sl<=l&&r<=sr)
	{
		// cout<<"l="<<l<<" r="<<r<<" sl="<<sl<<" sr="<<sr<<" dt="<<t[p].dt<<" lazy="<<nw_lazy*(r-l+1)<<"\n";
		return t[p].dt+nw_lazy*(r-l+1);
	}
	int mid=(l+r)>>1,ans=0;
	if(sl<=mid) ans+=find(l,mid,sl,sr,nw_lazy+t[p].lazy,t[p].lp);
	if(sr>mid) ans+=find(mid+1,r,sl,sr,nw_lazy+t[p].lazy,t[p].rp);
//	cout<<"l="<<l<<" r="<<r<<" sl="<<sl<<" sr="<<sr<<" ans="<<ans<<"\n";
	return ans;
	// pair<int,int> ans=make_pair(0,0);
}

int query(int l,int r,int t)
{
//	cout<<root[t]<<"\n";
	return find(1,n,l,r,0,root[t]);
}

signed main()
{
	// freopen("a.in","r",stdin);
	ios::sync_with_stdio(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i],f[i]=f[i-1]+a[i];
//	return 0;
	int nw=0;
	while(m--)
	{
		char op;
		int l,r,d,t;
		cin>>op;
		if(op=='C')
		{
			cin>>l>>r>>d;
			nw++;
			add(1,n,l,r,d,root[nw-1],root[nw]);
		}
		if(op=='Q')
		{
			cin>>l>>r;
			cout<<query(l,r,nw)+f[r]-f[l-1]<<"\n";
		}
		if(op=='H')
		{
			cin>>l>>r>>t;
			cout<<query(l,r,t)+f[r]-f[l-1]<<"\n";
		}
		if(op=='B')
		{
			cin>>t;
			for(int j=t+1;j<=nw;j++) root[j]=0;
			nw=t;
		}
	}
	//mt19937_64 myrand(time(0));
	return 0;
}

/*
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

*/

2. 有后效性的 dp

CF24D Broken robot

一般用高斯消元 \(O(n^3)\) 求解。

也可以多跑几遍朴素 dp 使误差降到可接受范围内。

多跑几遍的代码
#include<bits/stdc++.h>

using namespace std;

double dp[1005][1005];
int n,m,x,y;

int main()
{
	cin>>n>>m>>x>>y;
	for(int i=n-1;i>=x;i--)
	for(int kkk=1;kkk<=100;kkk++)
	{
		if(m==1) dp[i][1]=(dp[i][1]+dp[i+1][1])/2.0+1;
		else
		{
			dp[i][1]=(dp[i][1]+dp[i][2]+dp[i+1][1])/3.0+1;
			dp[i][m]=(dp[i][m]+dp[i][m-1]+dp[i+1][m])/3.0+1;
			for(int j=2;j<m;j++)
			dp[i][j]=(dp[i][j]+dp[i][j-1]+dp[i][j+1]+dp[i+1][j])/4.0+1;
		}
	}
	
	printf("%.10lf",dp[x][y]);
	
	return 0;
}

3. P14402 [JOISC 2016] 危险的滑冰 / Dangerous Skating

图论建模。

思考如何移动,即如何建图。无非就是两种方式:

  • 可以通过耗费 \(1\) 的代价走到上下左右连续最远的非冰块的格子。

  • 可以通过耗费 \(2\) 的代价走到相邻的格子。

HZOI2025 KingGojianOfYue's Solution

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif

const int fx[]={0,0,1,-1};
const int fy[]={1,-1,0,0};
int n,m;
char mp[1005][1005];
int id[1005][1005];
const int N=1e6+5;
vector<int> E[1<<20],V[1<<20];

bool vis[1<<20];
int ds[1<<20];
priority_queue<pair<int,int> ,vector<pair<int,int> >, greater<pair<int,int> > > pq;

void dij(int s)
{
	memset(vis,0,sizeof(vis));
	memset(ds,0x3f,sizeof(ds));
	
	ds[s]=0;
	pq.push(make_pair(ds[s],s));
	while(pq.size())
	{
		int nw=pq.top().second;
		pq.pop();
		if(vis[nw]==1) continue;
		
		vis[nw]=1;
		
		for(int i=0;i<E[nw].size();i++)
		{
			int k=E[nw][i];
			int w=V[nw][i];
			
			if(ds[k]>ds[nw]+w)
			{
				ds[k]=ds[nw]+w;
				pq.push(make_pair(ds[k],k));
			}
			
		}
	}
}

void add(int u,int v,int w)
{
	E[u].push_back(v);
	V[u].push_back(w);
}

void ch1(int x)
{
	int last=1;
	for(int i=1;i<=m;i++)
	{
		if(mp[x][i]=='#') { last=i+1; continue; }
		if(i==last) continue;
		add(id[x][i-1],id[x][i],2);
		add(id[x][i],id[x][last],1);
	}
	last=m;

	for(int i=m;i>=1;i--)
	{
		if(mp[x][i]=='#') { last=i-1; continue; }
		if(i==last) continue;
		add(id[x][i+1],id[x][i],2);
		add(id[x][i],id[x][last],1);
	}
}

void ch2(int y)
{
	int last=1;
	for(int i=1;i<=n;i++)
	{
		if(mp[i][y]=='#') { last=i+1; continue; }
		if(i==last) continue;
		add(id[i][y],id[i-1][y],2);
		add(id[i][y],id[last][y],1);
	}

	last=n;

	for(int i=n;i>=1;i--)
	{
		if(mp[i][y]=='#') { last=i-1; continue; }
		if(i==last) continue;
		add(id[i][y],id[last][y],1);
		add(id[i][y],id[i+1][y],2);
	}
}

signed main()
{
	// #ifndef ONLINE_JUDGE

	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	cin>>mp[i][j];

	int nw=0;
	for(int i=1;i<=1000;i++)
	for(int j=1;j<=1000;j++)
	id[i][j]=++nw;

	for(int i=1;i<=n;i++) ch1(i);
	for(int i=1;i<=m;i++) ch2(i);

	// for(int i=1;i<=n;i++)
	// for(int j=1;j<=m;j++)
	// {
	// 	if(mp[i][j]=='#') continue;
	// 	int nw=id[i][j];
	// 	for(int k=0;k<4;k++)
	// 	{
	// 		int tox=i+fx[k];
	// 		int toy=j+fy[k];
	// 		if(mp[tox][toy]=='#') continue;
	// 		if(tox<1||toy<1||tox>n||toy>m) continue;
	// 		// cout<<i<<" "<<j<<" "<<tox<<" "<<toy<<"\n";
			 
	// 		add(nw,id[tox][toy],2);
	// 	}
	// }

	int sx,sy,ex,ey;
	cin>>sx>>sy>>ex>>ey;
	int S=id[sx][sy];
	int T=id[ex][ey];

	dij(S);
	if(ds[T]>=0x3f3f3f3f) ds[T]=-1;
	cout<<ds[T]<<"\n";

	// #endif
	//mt19937_64 myrand(time(0));
	return 0;
}

4. 树上包含所有关键点的连通块最小大小

P9340 [JOIST 2023] 旅行 / Tourism

题意:树上问题,多次查询包含区间中所有点的连通块最小大小。

关键:包含所有关键点的连通块最小大小是经典问题,虚树中边的数量等于按 dfs 排序后两两相邻的点的距离之和。(第一个和最后一个也相邻)

由于与前驱后继有关,考虑使用链表维护只删不加回滚莫队,然后我们可以做到时间复杂度 \(O(n\sqrt{n})\)

点击查看代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;

const int N=3e5+5;
const int M=1e5+5;

int n,m,q;
int c[M];
// int u[N],v[N];

int cnt,dfn[M];
int tot,st[19][N];
// int id[N];
int dep[M];
int logn[N];
int L[M],R[M];
// int cnt=0;

vector<int> E[M];
int id[M];
const int len=500;
struct Q{
	int l,r,i;
}ask[M];
int ans[M];
bool cmp(Q x,Q y) { return id[x.l]==id[y.l]?x.r>y.r:x.l<y.l; }
int pre[100005],nxt[100005];

int sum=0;
int T[100005];
int num[100005];
int to[100005];
int head,tail;

void dfs(int p,int fa)
{
	++tot;
	++cnt;
	dfn[p]=cnt;
	dep[p]=dep[fa]+1;
	to[cnt]=p;

	st[0][tot]=dep[p];
	L[p]=tot;
	
	for(int to:E[p])
	{
		if(to==fa) continue;
		dfs(to,p);
		st[0][++tot]=dep[p];
	}
	R[p]=tot;
}

int lca(int l,int r)
{
	int k=logn[r-l+1];
	return min(st[k][l],st[k][r-(1<<k)+1]);
}

void del(int col)
{
	int l=pre[col],mid=col,r=nxt[col];
	if(l==-1) l=tail,head=r;
	if(r==n+1) r=head,tail=l;
	sum-=dep[l]+dep[mid]-2*lca(min(L[l],L[mid]),max(R[l],R[mid]));
	sum-=dep[mid]+dep[r]-2*lca(min(L[mid],L[r]),max(R[mid],R[r]));
	sum+=dep[l]+dep[r]-2*lca(min(L[l],L[r]),max(R[l],R[r]));	
	if(nxt[col]<=n) pre[nxt[col]]=pre[col];	
	if(pre[col]>0)  nxt[pre[col]]=nxt[col];
}

void baoli(int l,int r)
{
	if(l>r) return;
	num[c[l]]--;
	if(!num[c[l]])
	{
		int col=c[l];
		int lt=tail,lh=head;
		int pr=0,nx=0;
		if(nxt[col]<=n) pr=pre[nxt[col]];
		if(pre[col]>0) nx=nxt[pre[col]];
		del(c[l]);
		baoli(l+1,r);
		tail=lt;
		head=lh;
		if(nxt[col]<=n)pre[nxt[col]]=pr;
		if(pre[col]>0) nxt[pre[col]]=nx;
	}
	else baoli(l+1,r);
	num[c[l]]++;
}

void solve(int ql,int qr,int lid)
{
	const int sl=max((lid-1)*len,1);
	sum=head=tail=0;
	memset(num,0,sizeof(num));
	memset(T,0,sizeof(T));
	for(int i=1;i<=n;i++)
	{
		pre[i]=-1;
		nxt[i]=n+1;
	}

	for(int i=sl;i<=m;i++) num[c[i]]++;
	for(int i=sl;i<=m;i++) T[dfn[c[i]]]=1;
	int last=-1,first=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
	{
		if(T[i])
		{
			if(last>=0) pre[to[i]]=to[last];
			else pre[to[i]]=-1;
			first=min(first,i);
			if(last>0) sum+=dep[to[last]]+dep[to[i]]-2*lca(min(L[to[last]],L[to[i]]),max(R[to[last]],R[to[i]]));
			last=i;
		}
	}
	tail=to[last];
	sum+=dep[to[last]]+dep[to[first]]-2*lca(min(L[to[last]],L[to[first]]),max(R[to[last]],R[to[first]]));

	last=n+1;
	for(int i=n;i>=1;i--)
	{
		if(T[i])
		{
			if(last<=n) nxt[to[i]]=to[last];
			else nxt[to[i]]=n+1;
			last=i;
		}
	}
	head=to[last];
	int r=m;
	for(int i=ql;i<=qr;i++)
	{
		while(r>ask[i].r)
		{
			num[c[r]]--;
			if(num[c[r]]==0) del(c[r]);
			r--;
		}
		int summ=sum;
		baoli(sl,ask[i].l-1);
		ans[ask[i].i]=sum/2+1;
		sum=summ;
	}
}

bool cmp1(int l,int r){ return dfn[l]<dfn[r]; }

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
	for(int i=1;i<=100000;i++) id[i]=i/len+1;
	for(int i=2;i<(N);i++) logn[i]=logn[i>>1]+1;
	cin>>n>>m>>q;
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		E[u].push_back(v);
		E[v].push_back(u);
	}
	for(int i=1;i<=m;i++) cin>>c[i];
	dfs(1,0);

	for(int k=1;k<=logn[tot];k++)
	for(int i=1;i<=tot;i++)
	st[k][i]=min(st[k-1][i],st[k-1][i+(1<<(k-1))]);

	for(int i=1;i<=q;i++)
	{
		int l,r;
		cin>>l>>r;
		ask[i]={l,r,i};
	}

	 sort(ask+1,ask+1+q,cmp);
	for(int i=1;i<=q;i++)
	{
		int l=ask[i].l,r=ask[i].r;
		if(id[l]==id[r])
		{
			int a[505]={},len=r-l+1,sum=0;
			for(int i=l;i<=r;i++) a[i-l+1]=c[i];
			
			sort(a+1,a+1+len,cmp1);
			
			a[len+1]=a[1];
			for(int i=1;i<=len;i++)
				sum+=dep[a[i]]+dep[a[i+1]]-2*lca(min(L[a[i]],L[a[i+1]]),max(R[a[i]],R[a[i+1]]));
			
			ans[ask[i].i]=sum/2+1;
			continue;
		}
		int nw=i;
		 while(id[ask[i+1].l]==id[l]) i++;
		solve(nw,i,id[l]);
	}

	for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
	
	return 0;
}

5. 折半报警器。

P7603 [THUPC 2021] 鬼街

假设这个报警器要触发报警还需要 \(x\) 次闹鬼,这个报警器监控 \(k\) 个房子。

那么根据鸽巢原理,如果这个警报器触发警报,这个报警器监控的屋子中一定存在一个屋子闹鬼次数 \(\ge \lceil \frac{x}{k} \rceil\)。但是反之则不一定。

\(\frac{x}{k}\) 定义为报警阈值。

我们对每个房间开一个优先队列,记录每一个监视器在该房间处可能报警的闹鬼次数的阈值。

所以当一个屋子的闹鬼次数增加时,我们把那些可能会触发警报的警报器拿出来,判断是否触发警报,如果没触发,那么重新计算 \(x\),再往这 \(k\) 个房间的优先队列里添加新的报警阈值。

堆的删除可以使用懒惰删除,维护每个监视器的最新阈值编号(出队次数)、以及堆中每个阈值信息的编号即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;

inline ll read()
{
	ll x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(ll x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=1e5+5;
int n,m;
bitset<N> f;
int tot,prime[N];
vector<int> v[N];

void init()
{
	const int maxn=1e5;
	for(int i=2;i<=maxn;i++)
	{
		if(!f[i])
		{
			prime[++tot]=i;
			for(int j=i+i;j<=maxn;j+=i)
			f[j]=1;
		}
	}
	for(int i=2;i<=maxn;i++)
	{
		int x=i;
		for(int j=1;prime[j]*prime[j]<=x&&j<=tot;j++)
		if(x%prime[j]==0)
		{
			while(x%prime[j]==0) x/=prime[j];
			v[i].push_back(prime[j]);
		}
		if(x>1) v[i].push_back(x);
	}
}

int dep[N];
ll cnt[N];
#define pr pair<int,int> 
priority_queue<pair<ll,pr > ,vector<pair<ll,pr > >,greater<pair<ll,pr > > > q[N];
int jkqid[N],jkqtot;
ll ycnt[N];
int X[N];
ll val[N];
ll lastans=0;
bool vis[N];
vector<ll> ans;
#define mr(a,b) make_pair(a,b)

void add(int id,int x,ll y)
{
	ll lim=ceil(y*1.0/v[x].size());
	for(int i:v[x])
	{
		ycnt[id]+=cnt[i];
		q[i].push(mr(cnt[i]+lim,mr(0,id)));
	}
}

void check(int x)
{
	while(q[x].size()&&q[x].top().first<=cnt[x])
	{
		pair<ll,pr > nw=q[x].top();
		q[x].pop();		
		int p=nw.second.second;

		if(vis[p]) continue;
		if(dep[p]!=nw.second.first) continue;

		ll nwcnt=0;

		for(int i:v[X[p]]) nwcnt+=cnt[i];
		nwcnt-=ycnt[p];
		if(nwcnt>=val[p])
		{
			vis[p]=1;
			ans.push_back(p);
			continue;
		}
		nwcnt=val[p]-nwcnt;
		dep[p]++;

		ll lim=ceil(nwcnt*1.0/v[X[p]].size());
		for(int i:v[X[p]])
			q[i].push(mr(cnt[i]+lim,mr(dep[p],p)));
	}
}

void change(int x,ll y)
{
	for(int i:v[x]) cnt[i]+=y;
	for(int i:v[x]) check(i);
}


signed main()
{
	// #ifndef ONLINE_JUDGE
	// freopen("P7603.in","r",stdin);
	// freopen("P7603.out","w",stdout);

	n=read();
	m=read();
	init();
	
	for(int kkk=1;kkk<=m;kkk++)
	{
		int op=read(),x=read();
		ll y=read()^lastans;

		if(op==1)
		{
			jkqtot++;
			jkqid[kkk]=jkqtot;

			if(y==0) { ans.push_back(kkk); continue; }
			if(x==1) continue;

			X[kkk]=x;
			val[kkk]=y;
			add(kkk,x,y);
		}    
		else
		{
			lastans=0;
			change(x,y);
			lastans=ans.size();
			write(ans.size());
			putchar(' ');
			sort(ans.begin(),ans.end());
			for(int i:ans) write(jkqid[i]),putchar(' ');
			putchar('\n');
			ans.clear();
		}
	}
	
	return 0;
}

6. 扫描线 + 并查集维护值域连续段。

P7907 [Ynoi2005] rmscne

过于高妙,我也不会讲。

点击查看代码
#include<bits/stdc++.h>
// #define int long long
// #define ONLINE_JUDGE
#define lp (p<<1)
#define rp ((p<<1)|1)

using namespace std;

inline int read()
{
	int x=0,c=getchar_unlocked();
	for(;c>'9'||c<'0';c=getchar_unlocked());
	for(;c>='0'&&c<='9';c=getchar_unlocked())
		x=(x<<1)+(x<<3)+(c^48);
	return x;
}
inline void write(int x)
{
	// if(x<0) x=-x,putchar_unlocked('-');
	if(x>9)  write(x/10);
	putchar_unlocked(x%10+'0');
}

const int N=2e6+5,inf=1e9;
int n;
int a[N];
int las[N];

struct Tr{
	int lazy,minn;
}t[N<<2];
int m;
int f[N];

int find(int x)
{
    if(x==f[x]) return x;
    return f[x]=find(f[x]);
}

void push_down(int l,int mid,int r,int p)
{
    if(!t[p].lazy) return;
    t[lp].minn=t[p].lazy-mid+1;
    t[rp].minn=t[p].lazy-r+1;
    t[lp].lazy=t[p].lazy;
    t[rp].lazy=t[p].lazy;
    t[p].lazy=0;
}

struct Ans{
    int l,id;
};
vector<Ans> v[N];
int ans[N];

void build(int l,int r,int p)
{
    t[p].minn=inf;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,lp);
    build(mid+1,r,rp);
}

void change(int l,int r,int sl,int sr,const int qr,int p)
{
    if(sl<=l&&r<=sr)
    {
        t[p].lazy=qr;
        t[p].minn=qr-r+1;
        return;
    }
    int mid=(l+r)>>1;
    push_down(l,mid,r,p);
    if(sl<=mid) change(l,mid,sl,sr,qr,lp);
    if(sr>mid) change(mid+1,r,sl,sr,qr,rp);
    t[p].minn=min(t[lp].minn,t[rp].minn);
}

int query(int l,int r,int sl,int sr,int p)
{
    if(sl<=l&&r<=sr) return t[p].minn;
    int mid=(l+r)>>1,ans=inf;
    push_down(l,mid,r,p);
    if(sl<=mid) ans=query(l,mid,sl,sr,lp);
    if(sr>mid) ans=min(ans,query(mid+1,r,sl,sr,rp));
    return ans;
}

signed main()
{
	// #ifndef ONLINE_JUDGE
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
	// #endif
	
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),f[i]=i;
	
	m=read();
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read();
		if(l>r) swap(l,r);
		v[r].emplace_back(Ans{l,i});
	}

    build(1,n,1);

    for(int i=1;i<=n;i++)
    {
        f[las[a[i]]]=las[a[i]]+1;
        change(1,n,las[a[i]]+1,i,i,1);

        for(Ans j:v[i]) ans[j.id]=query(1,n,j.l,find(j.l),1);

        las[a[i]]=i;
    }

    for(int i=1;i<=m;i++)
    {
        write(ans[i]);
        putchar_unlocked('\n');
    }
	
	//mt19937_64 myrand(time(0));
	return 0;
}

7. 分治优化区间背包问题

P6240 好吃的题目

先考虑不跨过分治中点的贡献,递归回来后再考虑跨过分治中点的贡献,然后合并背包或者暴力跑背包。

更像猫树的写法
#include<bits/stdc++.h>
// #define int long long
// #define double long double
// using namespace std;
using std::vector;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar();
	for(;c>'9'||c<'0';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return x;
}
inline void write(int x)
{
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n;
int logn[(1<<17)|1];
int ans[200005];

struct Node{
    int l,r,t,id;
};
vector<Node> v[40005*17];
int h[80000],w[80000];
int m;
int dp1[65999][201];
int dp2[65999][201];
int pos[1<<20];

inline int max(const int &x,const int &y) { return x>y?x:y; }

using std::cout;

void solve(int l,int r,int p)
{
    if(l==r)
    {
        for(int i=h[l];i<=200;i++) dp1[l][i]=dp2[l][i]=w[l];
        return;
    }
    int mid=(l+r)>>1,lp=p<<1,rp=(p<<1)|1;
    solve(l,mid,lp);
    solve(mid+1,r,rp);

    for(int i=0;i<v[p].size();i++)
    {
        int nw=0;
        for(int k=0;k<=v[p][i].t;k++) nw=max(nw,dp1[v[p][i].r][k]+dp2[v[p][i].l][v[p][i].t-k]);
        // cout<<"id="<<v[p][i].id<<" nw="<<nw<<"\n";
        ans[v[p][i].id]=nw;
    }

    for(int i=mid;i>=l;i--)
    for(int j=200;j>=0;j--)
    {
        if(j>=h[i]) dp2[i][j]=max(dp2[i+1][j],dp2[i+1][j-h[i]]+w[i]);
        else dp2[i][j]=dp2[i+1][j];
    }

    for(int i=mid+1;i<=r;i++)
    for(int j=200;j>=0;j--)
    {
        if(j>=h[i]) dp1[i][j]=max(dp1[i-1][j],dp1[i-1][j-h[i]]+w[i]);
        else dp1[i][j]=dp1[i-1][j];
    }

    // for(int i=l;i<=r;i++)
    // for(int j=0;j<=200;j++)
    // dp[i][j]=dp[op^1][i][j];

    // for(int i=mid+1;i<=r;i++) 
}

signed main()
{
    logn[0]=-1;
    for(int i=1;i<=(1<<17);i++) logn[i]=logn[i>>1]+1;
    logn[0]=0;

    n=read();
    m=read();
    for(int i=1;i<=n;i++) h[i]=read();
    for(int i=1;i<=n;i++) w[i]=read();

    int R=(1<<(logn[n]+1));
    if(n==(1<<logn[n])) R=n;
    for(int i=1,nw=(1<<logn[R])-1;i<=R;i++)
    pos[i]=i+nw;
    // cout<<"i="<<i<<" pos="<<pos[i]<<"\n";

    for(int i=n+1;i<=R;i++) h[i]=200,w[i]=0;

    for(int i=1;i<=m;i++)
    {
        int l=read(),r=read(),t=read();
        // int id=(pos[l]>>logn[pos[l]^pos[r]]);
        // int id=(l>>(logn[l^(r>>(logn[r]-logn[l]))]+1));
        int id=(pos[l]>>(logn[pos[l]^pos[r]]+1));
        // cout<<"l="<<l<<" r="<<r<<" id="<<id<<"\n";//" (logn[r]-logn[l])="<<(logn[r]-logn[l])<<" (r>>(logn[r]-logn[l]))="<<(r>>(logn[r]-logn[l]))<<" (l^(r>>(logn[r]-logn[l])))="<<(l^(r>>(logn[r]-logn[l])))<<"\n";
        if(l==r) ans[i]=(t>=h[l])*w[l];
        else v[id].push_back({l,r,t,i});
    }
    // std::cout<<1<<" "<<R<<"\n";
    solve(1,R,1);

    for(int i=1;i<=m;i++)
    {
        write(ans[i]);
        putchar('\n');
    }
    return 0;
}
更像整体二分的写法(不是这道题)
#include<bits/stdc++.h>

using namespace std;

inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif

const int N=1e5+505;
const int mod=998244353;
const int MAXN=505;
int n;
int v[N],w[N];
struct Node{
	int l,r,m,id;
}a[N],a1[N],a2[N],a3[N];
// int ans1[N],ans2[N];
int q;
#define lp (p<<1)
#define rp ((p<<1)|1)
#define ll long long
struct Dp{
	int v;
	int cnt;
}dpl[20005][505],dpr[20005][505],ans[N],sum[MAXN+10]; // 以 L 为下标
Dp operator+(const Dp &x,const Dp &y) { return Dp{x.v+y.v,(x.cnt+y.cnt>=mod)?(x.cnt+y.cnt-mod):(x.cnt+y.cnt) }; }
Dp operator*(const Dp &x,const Dp &y) { return Dp{x.v+y.v,int(((long long)x.cnt)*y.cnt%mod)}; }

void solve(int ql,int qr,int L,int R,int p)
{
	if(L>R) return;
	if(L==R)
	{
		for(int i=0;i<MAXN;i++) dpl[L][i]=dpr[L][i]={0,0};
		dpl[L][0]=dpr[L][0]={0,1};
		dpl[L][w[L]]=dpr[L][w[L]]={v[L],1};
		for(int i=ql;i<=qr;i++)
		if(a[i].m>=w[L]) ans[a[i].id]={v[L],1};
		else ans[a[i].id]={0,0};//,cout<<"id="<<a[i].id<<" m="<<a[i].m<<"\n";
		return;
	}
	int mid=(L+R)>>1,cnt1=0,cnt2=0,cnt3=0,nw=ql;
	for(int i=ql;i<=qr;i++)
	{
		if(a[i].r<=mid) a1[++cnt1]=a[i]; // 左边
		else if(a[i].l>mid) a2[++cnt2]=a[i]; // 右边
		else a3[++cnt3]=a[i]; // 跨中点
	}
	for(int i=1;i<=cnt1;i++) a[nw++]=a1[i]; 
	for(int i=1;i<=cnt2;i++) a[nw++]=a2[i]; 
	for(int i=1;i<=cnt3;i++) a[nw++]=a3[i];
	solve(ql,ql+cnt1-1,L,mid,lp);
	solve(ql+cnt1,ql+cnt1+cnt2-1,mid+1,R,rp);
	// cout<<"\n--------------------------------\n\n";
	// cout<<"L="<<L<<" R="<<R<<"\n";
	for(int i=ql;i<=qr;i++)
	if(a[i].l<=mid&&a[i].r>mid)
	{
		Dp nw={0,0};
		sum[0]=dpl[a[i].r][0];
		for(int sizr=1;sizr<=a[i].m;sizr++)
		{
			sum[sizr]=sum[sizr-1];
			if(sum[sizr].v<dpl[a[i].r][sizr].v) sum[sizr]={dpl[a[i].r][sizr].v,0};
			if(sum[sizr].v==dpl[a[i].r][sizr].v) (sum[sizr].cnt+=dpl[a[i].r][sizr].cnt)%=mod;
		}
		// cout<<"!!! id="<<a[i].id<<" m="<<a[i].m<<" \n";
		for(int sizl=0;sizl<=a[i].m;sizl++)
		{
			int maxnr=a[i].m-sizl;
			// for(int sizr=0;sizr<=maxnr;sizr++)
			// {
				Dp to=dpr[a[i].l][sizl]*sum[maxnr];
				// cout<<"sizl="<<sizl<<" sizr="<<sizr<<" to: v="<<to.v<<" cnt="<<to.cnt<<" [l,mid]: v="<<dpr[a[i].l][sizl].v<<" cnt="<<dpr[a[i].l][sizl].cnt<<" [mid+1,r]: v="<<dpl[a[i].r][sizr].v<<" cnt="<<dpl[a[i].r][sizr].cnt<<"\n";
				if(to.v>nw.v) nw={to.v,0};
				if(to.v==nw.v) (nw.cnt+=to.cnt)%=mod;
			// }
		}
		// if(dp)
		ans[a[i].id]=nw;
	}

	for(int i=mid+1;i<=R;i++)
	{
		for(int j=0;j<w[i];j++) dpl[i][j]=dpl[i-1][j];
		for(int j=w[i];j<MAXN;j++)
		{
			int to=v[i]+dpl[i-1][j-w[i]].v;
			if(to>=dpl[i-1][j].v) dpl[i][j]=dpl[i-1][j-w[i]]+Dp{v[i],0}; 
			if(to==dpl[i-1][j].v) (dpl[i][j].cnt+=dpl[i-1][j].cnt)%=mod;
			// else if(dpl[i-1][j].v==to) dpl[i][j]=dpl[i-1][j]+(dpl[i-1][j-w[i]]+Dp{v[i],0}); 
			if(to<dpl[i-1][j].v) dpl[i][j]=dpl[i-1][j];
		}
	}

	for(int i=mid;i>=L;i--)
	{
		for(int j=0;j<w[i];j++) dpr[i][j]=dpr[i+1][j];
		for(int j=w[i];j<MAXN;j++)
		{
			int to=v[i]+dpr[i+1][j-w[i]].v;
			if(to>=dpr[i+1][j].v) dpr[i][j]=dpr[i+1][j-w[i]]+Dp{v[i],0}; 
			if(to==dpr[i+1][j].v) (dpr[i][j].cnt+=dpr[i+1][j].cnt)%=mod;
			if(to<dpr[i+1][j].v) dpr[i][j]=dpr[i+1][j];
		}
	}
}

signed main()
{
	// #ifndef ONLINE_JUDGE
	freopen("knapsack.in","r",stdin);
	freopen("knapsack.out","w",stdout);
	
	n=read();
	for(int i=1;i<=n;i++) v[i]=read(),w[i]=read();
	q=read();
	for(int i=1;i<=q;i++)
	{
		int l=read(),r=read(),m=read();
		a[i]={l,r,m,i};
	}
	solve(1,q,1,n,1);
	for(int i=1;i<=q;i++)
	write(ans[i].v),putchar(' '),write(ans[i].v?ans[i].cnt:0),putchar('\n');

	// #endif
	//mt19937_64 myrand(time(0));
	return 0;
}


8. 拉格朗日插值优化 dp

P4463 [集训队互测 2012] calc

适用于转移是卷积的形式。

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int k,n,p;
int dp[5005][5005];
int frac(int n,int p)
{
    int ans=1;
    for(int i=2;i<=n;i++) ans*=i,ans%=p;
    // cout<<ans<<"\n";
    return ans;
}

int ksm(int x,int p,int mod)
{
    int ans=1;
    int f=1;
    if(x<0) x=-x,f=-1;
    while(p)
    {
        if(p&1) ans*=x,ans%=mod;
        x*=x;
        x%=mod;
        p>>=1;
    }
    return (ans*f+mod)%mod;
}

int lagerage(int id,int n,int k,int mod)
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int nw=1;
        for(int j=1;j<=n;j++)
        {
            if(i==j) continue;
            nw*=(k-j)*ksm(i-j,mod-2,mod)%mod;
            nw%=mod;
        }
        nw*=dp[id][i];
        nw%=mod;
        ans+=nw;
        ans%=mod;
    }
    return (ans%mod+mod)%mod;
}

signed main()
{
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	#endif

    k=read();
    n=read();
    p=read();
    // dp[0][0]=1;
    for(int i=0;i<=n*3;i++) dp[0][i]=1;     
    for(int i=1;i<=n;i++)
    {
        // dp[i][0]=1;
        for(int j=1;j<=n*3;j++)
        {
            // if(i==1&&j==1) {dp[i][j]=1; continue;}
            dp[i][j]=(dp[i-1][j-1]*j+dp[i][j-1])%p;
            // cout<<dp[i][j]<<"\n";
        }        
    }

    //1881
    cout<<(lagerage(n,n*3,k,p)*frac(n,p))%p;

	//mt19937_64 myrand(time(0));
	return 0;
}

9. 不去重离散化

适用于一类权值相同的元素也可以造成贡献的问题。

可以配合 bitset(手写) 。

例题:

经典例题:P4688 [Ynoi Easy Round 2016] 掉进兔子洞

(非正解)需要配合手写 bitset 求权值第 \(k\) 小。P3242 [HNOI2015] 接水果


10. 区间 LCA 深度求和问题

P4211 [LNOI2014] LCA

可以转化为根链上每个点加 \(1\),根链查询点权和。

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=1e5+5;
int n,m;
vector<int> E[N];
int fa[N];
int siz[N],dfn[N],top[N],son[N],dep[N],tot;
int sum[N<<2],lazy[N<<2];

#define pushdown()                          \
    {                                       \
        lazy[lp] += lazy[p];                \
        lazy[rp] += lazy[p];                \
        sum[lp] += lazy[p] * (mid - l + 1); \
        sum[rp] += lazy[p] * (r - mid);     \
        lazy[p] = 0;                        \
    }

void add(int l,int r,int sl,int sr,int k,int p)
{
	if(sl<=l&&r<=sr)
	{
		sum[p]+=k*(r-l+1);
		lazy[p]+=k;
		return;
	}
	int lp=(p<<1),rp=(p<<1)|1,mid=(l+r)>>1;
	if(lazy[p]) pushdown()
	
	if(sl<=mid) add(l,mid,sl,sr,k,lp);
	if(sr>mid) add(mid+1,r,sl,sr,k,rp);
	sum[p]=sum[lp]+sum[rp];
}

int query(int l,int r,int sl,int sr,int p)
{
    if(sl<=l&&r<=sr) return sum[p];
    
	int lp=(p<<1),rp=(p<<1)|1,mid=(l+r)>>1,ans=0;
	if(lazy[p]) pushdown()
	
	if(sl<=mid) ans=query(l,mid,sl,sr,lp);
	if(sr>mid) ans+=query(mid+1,r,sl,sr,rp);
	sum[p]=sum[lp]+sum[rp];
    return ans;
}

void dfs1(int p,int fa)
{
    siz[p]=1;
    dep[p]=dep[fa]+1;
    for(int to:E[p])
    {
        if(to==fa) continue;
        dfs1(to,p);
        // cerr<<p<<" "<<to<<"\n";
        siz[p]+=siz[to];
        // cerr<<son[p]<<" "<<siz[son[p]]<<" "<<siz[to]<<"\n";
        if(siz[to]>siz[son[p]]) son[p]=to;
    }
}

void dfs2(int p,int tp)
{
    dfn[p]=++tot;
    // add(1,n,tot,tot,0,1);
    top[p]=tp;
    if(son[p]) dfs2(son[p],tp);

    for(int to:E[p])
    if(!dfn[to]) dfs2(to,to);
}

int query(int u)
{
    // if(u==3) return 0;
    int ans=0;
    while(top[u]!=1) ans+=query(1,n,dfn[top[u]],dfn[u],1),u=fa[top[u]];//,cerr<<"u "<<u<<"\n";
    ans+=query(1,n,dfn[1],dfn[u],1);
    return ans;
}

void add(int u)
{
    while(top[u]!=1) add(1,n,dfn[top[u]],dfn[u],1,1),u=fa[top[u]];
    add(1,n,dfn[1],dfn[u],1,1);
}

struct Qu{
    int pos,op,z,id;
}ask[N<<1];
int cnt;
int ans[N];

bool cmp(Qu x,Qu y) { return x.pos<y.pos; }

signed main()
{
    n=read();
    m=read();
    for(int i=2;i<=n;i++)
    {
        fa[i]=read()+1;
        E[fa[i]].push_back(i);
    }
    dfs1(1,0);
    dfs2(1,1);
    // for(int i=1;i<=n;i++) cerr<<son[i]<<" ";
    // cout<<"\n";

    for(int i=1;i<=m;i++)
    {
        int l=read()+1,r=read()+1,z=read()+1;
        ask[++cnt]={l-1,-1,z,i};
        ask[++cnt]={r,1,z,i};
    }
    sort(ask+1,ask+1+cnt,cmp);
    // return 0;

    int nw=0;
    for(int i=1;i<=cnt;i++)
    {
        // cerr<<"i="<<i<<" ask[i].pos="<<ask[i].pos<<" nw="<<nw<<" z="<<ask[i].z<<"\n";
        while(nw<ask[i].pos)
        {
            // cerr<<"iubsdfbid";
            nw++;
            add(nw);
        }
        ans[ask[i].id]+=ask[i].op*query(ask[i].z);
    }
    for(int i=1;i<=m;i++) write(ans[i]%201314),putchar('\n');

	return 0;
}

11. 绝对众数问题:摩尔投票法

P3765 总统选举

基本思想:

注意到这样一个现象:在任何数组中,出现次数大于该数组长度一半的值只能有一个。

摩尔投票法的基本思想很简单,在每一轮投票过程中,从数组中找出一对不同的元素,将其从数组中删除。

这样不断的删除直到无法再进行投票,如果数组为空,则没有任何元素出现的次数超过该数组长度的一半。如果只存在一种元素,那么这个元素则可能为目标元素。

我们可以用线段树来维护这个过程。注意左右儿子剩的元素相同时的 pushup。

点击查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar_unlocked('-');
	if(x>9)  write(x/10);
	putchar_unlocked(x%10+'0');
}

const int N=5e5+5;
int n,m;
int a[N];
struct Tr{
	int val,cnt;
}t[N<<2];

#define lp (p<<1)
#define rp ((p<<1)|1)

void pushup(Tr &p,const Tr Lp,const Tr Rp)
{
	if(Lp.val==Rp.val)  ////////////////////  <----------------------
	{
		p.cnt=Lp.cnt+Rp.cnt;
		p.val=Lp.val;
		return;
	}
	if(Lp.cnt>Rp.cnt)
	{
		p.cnt=Lp.cnt-Rp.cnt;
		p.val=Lp.val;
	}
	else
	{
		p.cnt=Rp.cnt-Lp.cnt;
		p.val=Rp.val;
	}
}

void build(int l,int r,int p)
{
	if(l==r)
	{
		t[p].cnt=1;
		t[p].val=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,lp);
	build(mid+1,r,rp);
	pushup(t[p],t[lp],t[rp]);
}

void add(int l,int r,int x,int p)
{
	if(l==r)
	{
		t[p].cnt=1;
		t[p].val=a[l];
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) add(l,mid,x,lp);
	else add(mid+1,r,x,rp);
	pushup(t[p],t[lp],t[rp]);
}

Tr query(int l,int r,int sl,int sr,int p)
{
	if(sl<=l&&r<=sr) return t[p];
	int mid=(l+r)>>1;
	Tr ans={0,0};
	if(sl<=mid) pushup(ans,ans,query(l,mid,sl,sr,lp));
	if(sr>mid) pushup(ans,ans,query(mid+1,r,sl,sr,rp));
	return ans;
}

tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> s[N];

signed main()
{
	n=read();
	m=read();
	for(int i=1;i<=n;i++) a[i]=read(),s[a[i]].insert(i);
	for(int i=1;i<=n;i++) s[i].insert(n+1);
	build(1,n,1);
	while(m--)
	{
		int l=read(),r=read(),win=read(),k=read();
		Tr ans=query(1,n,l,r,1);
		if(ans.cnt)
		{
			int cnt=s[ans.val].order_of_key(*s[ans.val].upper_bound(r))-s[ans.val].order_of_key(*s[ans.val].lower_bound(l));			
			if(cnt>((r-l+1)>>1)) win=ans.val;
		} 
		while(k--)
		{
			int x=read();
			s[a[x]].erase(x);
			a[x]=win;
			s[a[x]].insert(x);
			add(1,n,x,1);
		}
		write(win);
		putchar_unlocked('\n');
	}
	Tr ans=query(1,n,1,n,1);
	if(ans.cnt==0) ans.val=-1;
	else if(s[ans.val].order_of_key(*s[ans.val].upper_bound(n))-s[ans.val].order_of_key(*s[ans.val].lower_bound(1))<=((n)>>1)) ans.val=-1;
	write(ans.val);
	putchar_unlocked('\n');
	return 0;
}

12. 反射容斥

双线板子题:P3266 [JLOI2015] 骗我呢

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=3e6+10;
const int mod=1e9+7;
int ksm(int x,int p)
{
	int ans=1;
	while(p)
	{
		if(p&1) ans*=x,ans%=mod;
		x*=x;
		x%=mod;
		p>>=1;
	}
	return ans;
}
int f[N],g[N];

void init()
{
	f[0]=g[0]=1;
	for(int i=1;i<N;i++)
	{
		f[i]=(f[i-1]*i)%mod;
		g[i]=ksm(f[i],mod-2);
	}
}

int n,m;
int ans;

int C(int n,int m)
{
	if(n<0||m<0) return 0;
	return f[n]*g[m]%mod*g[n-m]%mod;
}

void trans1(int &x,int &y)
{
	swap(x,y);
	x--;
	y++;
}
void trans2(int &x,int &y)
{
	swap(x,y);
	x+=m+2;
	y-=(m+2);
}

signed main()
{
	init();
	n=read();
	m=read();

	ans=C(n+m+1+n,n);
	// cout<<ans<<"\n";
	int x=n+m+1,y=n;
	while(x>=0&&y>=0)
	{
		// cout<<x<<" "<<y<<"\n";
		trans2(x,y);
		// cout<<x<<" "<<y<<"\n";
		if(x>=0&&y>=0) ans-=C(x+y,x),ans%=mod;
		else break;
		
		trans1(x,y);
		// cout<<x<<" "<<y<<"\n\n";
		if(x>=0&&y>=0) ans+=C(x+y,x),ans%=mod;
		else break;
	}
	// cout<<ans<<"\n";

	x=n+m+1,y=n;
	while(x>=0&&y>=0)
	{
		trans1(x,y);
		if(x>=0&&y>=0) ans-=C(x+y,x),ans%=mod;
		else break;
		
		trans2(x,y);
		if(x>=0&&y>=0) ans+=C(x+y,x),ans%=mod;
		else break;
	}

	cout<<(ans%mod+mod)%mod;

    return 0;
}

13. 广义(?)矩阵树定理

P3317 [SDOI2014] 重建

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endif

#define double long double

const int N=55;
double a[N][N];
double dis[N][N];
int fa[N];
int n;
int find(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}

const double eps=1e-12;

void output()
{

		for(int i=1;i<=n+1;i++,cout<<"\n")
		for(int j=1;j<=n+1;j++) 
		cout<<a[i][j]<<" ";
		cout<<"\n\n";
}

double Guess()
{
	n--;
	double ans=1,w=1;
	for(int i=1;i<=n;i++)
	for(int j=i+1;j<=n;j++)
	{
		if(abs(a[i][i])<eps) continue;
		double dt=a[j][i]/a[i][i];
		for(int k=i;k<=n;k++)
		a[j][k]-=dt*a[i][k];
		
		// output();
	}

	for(int i=1;i<=n;i++) ans*=a[i][i];
	for(int i=1;i<=n+1;i++)
	for(int j=1;j<i;j++)
	ans*=1-dis[i][j]+eps;
	return ans;
}

signed main()
{
	for(int i=1;i<N;i++) fa[i]=i;
	cin>>n;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	{
		cin>>dis[i][j];
		a[i][i]+=dis[i][j]/(1-dis[i][j]+eps);
		a[i][j]-=dis[i][j]/(1-dis[i][j]+eps);
		// cout<<a[i][j]<<" ";
		if(dis[i][j]>eps)
			if(find(i)!=find(j)) fa[find(j)]=find(i);
	}
	int faa=find(1);
	for(int i=2;i<=n;i++) if(find(i)!=faa) { cout<<0; return 0; }
	double ans=Guess(),ans2=0.000100573;
	assert((int)(ans2*9)!=100573);
	cout<<ans<<"\n";
	

	// #ifndef ONLINE_JUDGE
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
	// #endif
	//mt19937_64 myrand(time(0));
	return 0;
}

14. 去重方案数问题:

一般是具体问题具体分析。一般是贪心消除重复贡献或者减去重复贡献。

P12930 [USACO4.3] 逢低吸纳 Buy Low, Buy Lower 加强版

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n;
int a[1<<20],b[1<<20];
int tot=0;
unordered_map<int,int> mp;
const int mod=1e9+7;

int lowbit(int x) { return x&(-x); }
int t[1<<20];
int maxn;
int cnt[1<<20];
int sum[1<<20];

pair<int,int> query(int x)
{
    int ans=1,maxn=0;
    for(int i=x;i>0;i-=lowbit(i))
    {
        if(t[i]>maxn) maxn=t[i],ans=0;
        if(t[i]==maxn) ans+=sum[i];
    }
    return make_pair(maxn,ans%mod);
}

void add(int p,int maxn,int k)
{
    for(int i=p;i<=n;i+=lowbit(i))
    {
        if(t[i]<maxn) t[i]=maxn,sum[i]=0;
        if(t[i]==maxn) sum[i]+=k,sum[i]%=mod;
    }
}

signed main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=b[i]=read();
    sort(b+1,b+1+n);

    b[n+1]=-1;
    for(int i=1;i<=n;i++)
    if(b[i]!=b[i+1]) mp[b[i]]=++tot;

    for(int i=1;i<=n;i++) a[i]=mp[a[i]];
    for(int i=1;i<=n;i++) a[i]=tot-a[i]+1;

    maxn=tot;

    memset(cnt,-1,sizeof(cnt));
    for(int i=1;i<=n;i++)
    {
        pair<int,int> nw=query(a[i]-1);
        if(cnt[a[i]]==nw.first+1) continue;
        cnt[a[i]]=nw.first+1;
        add(a[i],cnt[a[i]],nw.second);
    }
    pair<int,int> ans=query(n);
    cout<<ans.first<<" "<<ans.second;
    
	return 0;
}

15. 神秘交互题 P12421 【MX-X12-T4】「ALFR Round 5」游戏

做过了还是想不到。

考察题目的性质,发现(?叶子结点的答案一次查询就可以得知。

然后没了。

点击查看代码
#include<bits/stdc++.h>
// #define int long long

using namespace std;

const int N=1e5+5;
int n,m;
vector<int> E[N];
int f[N];
int in[N];
void add(int u,int v) { E[u].push_back(v); E[v].push_back(u); }

queue<int> q,q2;

void dfs(int p,int fa)
{
    if(fa) f[p]=fa,in[fa]++;
    for(int to:E[p])
    if(to!=fa) dfs(to,p);
}

void solve()
{
	cin>>n>>m;
	for(int i=1,u,v;i<n;i++)
	{
		cin>>u>>v;
		add(u,v);
	}
    if(n==1)
    {
        cout<<"! "<<1<<endl;
        int xxxx;
        cin>>xxxx;
        if(xxxx==0) exit(0);
        return;
    }

	dfs(1,0);
    for(int i=1;i<=n;i++)
    if(!in[i]) q.push(i);


    for(int i=1;i<=n-1;i++)
    {
        // for(int i=1;i<=n ;i++)  cout<<in[i]<<" ";
        // cout<<"\n";
        int nw_ask=q.front();
        cout<<"? "<<nw_ask<<endl;
        q.pop();
        int op;int x;
        cin>>op;
        if(op==1)
        {
            while(q.size())
            {
                int x=q.front();
                // cout<<"x="<<x<<"\n";
                q.pop();
                in[f[x]]--;
                if(in[f[x]]==0&&f[x]) q2.push(f[x]);
            }
            q2.push(nw_ask);
            swap(q,q2);
            while(q2.size()) q2.pop();
        }
        if(op==2)
        {
            cin>>x;
            if(x==0)
            {
                while(q.size()) q.pop();
                q.push(nw_ask);
                break;
            }
            in[f[nw_ask]]--;
            if(in[f[nw_ask]]==0&&f[nw_ask]) q.push(f[nw_ask]);
        }
    }

    cout<<"! "<<q.front()<<endl;
    int xxxx;
    cin>>xxxx;
    if(xxxx==0) exit(0);

	for(int i=1;i<=n;i++)
	{
		in[i]=0;
		E[i].clear();
	}
    while(q.size()) q.pop();
    while(q2.size()) q2.pop();
}

signed main()
{
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	#endif
	
	int T;
	cin>>T;

	while(T--) solve();
	//mt19937_64 myrand(time(0));
	return 0;
}

16. 将区间查询变为左闭右开(左开右闭),再拆询问为两个单点询问,最后从左到右扫询问

经典 Trick,非常 nb。

左闭右开:P11830 [省选联考 2025] 幸运数字

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
 
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c<'0'||c>'9';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
	x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}

inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar(x%10+'0');
} 

struct Node{
	int pos;//�˵�
	int op;//����˵㻹���Ҷ˵�
	int la,ra;//���� 
}a[1<<20];
int tot;
bool cmp(Node x,Node y) { return x.pos<y.pos; }

struct NODE{
	int lnum,rnum;
}l,r,mid;
//int lmin=99999999999999;

bool check()
{
//	if(l.rnum>=r.lnum||r.rnum>=l.lnum) return 1;
	
//	if(l.lnum==0)
//	else if(r.lnum==0)
	if(l.rnum<r.lnum)
	{
//	cout<<"   1111   ";
		int num=l.rnum+r.lnum+mid.rnum;
		int pos=(num+1)/2;
		
		if(l.rnum<pos&&l.rnum+mid.rnum>=pos) return 1;
		else return 0; 
	}
	else if(r.rnum<l.lnum)
	{
//cout<<"   2222   ";
		int num=l.lnum+r.rnum+mid.rnum; 
		int pos=(num+1)/2;
//		cout<<num<<" "<<pos<<"\n ";
//		cout<<l.lnum<<" "<<r.rnum<<" "<<mid.rnum<<'\n';
		
		if(l.lnum<pos&&l.lnum+mid.rnum>=pos) return 1;
		else return 0; 
	}
	else return mid.rnum>0;
	
//	if(l.rnum<r.lnum)
//	else
}

void solve()
{
	l.lnum=0;
	l.rnum=0;
	mid.lnum=0;
	mid.rnum=0;
	r.lnum=0;
	r.rnum=0;
	tot=0;
	
	int n=read();
	for(int i=1;i<=n;i++)
	{
		int l1=read(),r1=read(),l2=read(),r2=read();
		a[++tot]={l2,1,l1,r1};
		a[++tot]={r2+1,2,l1,r1};
		r.lnum+=l1;
		r.rnum+=r1;
//		lmin=min(l1,lmin);
	}
	sort(a+1,a+1+tot,cmp);
//	a[tot+1].pos=a[tot].pos+1;
	for(int i=1;i<=tot;i++)
	{
//		if(a[i].op==1/*&&a[i].pos>lmin*/)
//		{
//			r.lnum+=a[i].la;
//			r.rnum+=a[i].ra;
//		}
//		
//		cout<<"i="<<i<<" pos="<<a[i].pos<<" op="<<a[i].op<<" la="<<a[i].la<<" ra="<<a[i].ra<<"\n"; 
	}
	
	
	
	int i=1,ans=0;
	while(i<=tot)
	{
//		cout<<i<<" ";
		int last=i;
		if(a[i].op==1)
		{
			r.lnum-=a[i].la;
			r.rnum-=a[i].ra;
			
			mid.lnum+=a[i].la;
			mid.rnum+=a[i].ra; 
		}
		if(a[i].op==2) 
		{
			l.lnum+=a[i].la;
			l.rnum+=a[i].ra;
			
			mid.lnum-=a[i].la;
			mid.rnum-=a[i].ra; 
		}
		i++;
		while(i<=tot&&a[i].pos==a[i-1].pos)
		{
			if(a[i].op==1)
			{
				r.lnum-=a[i].la;
				r.rnum-=a[i].ra;
				
				mid.lnum+=a[i].la;
				mid.rnum+=a[i].ra; 
			}
			if(a[i].op==2) 
			{
				l.lnum+=a[i].la;
				l.rnum+=a[i].ra;
				
				mid.lnum-=a[i].la;
				mid.rnum-=a[i].ra; 
			}
			i++;
		}
//		cout<<i<<" ";
//		if(i==4) check();
		if(check()) ans+=a[i].pos-a[last].pos;/*,cout<<a[i].pos-a[last].pos<<"\n";*///ans+=a[i].pos-1-(a[last].pos-1); 
//		cout<<"\n";
	}
	
	write(ans);
	putchar('\n'); 
}

signed main()
{
	// freopen("lucky.in","r",stdin);
	// freopen("lucky.out","w",stdout);
	
	int c=read(),T=read();
	
	while(T--) solve();
	
	return 0;
}

/*
0  1 
4
59420 59426 56645191 58528280
59138 59228 612897237 783734096
59074 59172 60329631 105436362
59460 59511 427126574 523858505


*/

17. 一种非恰好 \(n\) 个的矩阵优化的解题方式

思路是在答案矩阵后再拼接一个答案矩阵,最后查询时求矩阵中一段元素的和即可。

P10581 [蓝桥杯 2024 国 A] 重复的串

my sol


18. 神秘树形 dp

\(dp_i\) 为将子树染黑还需要多少次操作。

P3554 [POI 2013] LUK-Triumphal arch

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=3e5+5;
int n;
vector<int> E[N];
int son[N];

void dfs1(int p,int fa)
{
	for(int to:E[p])
	{
		if(to==fa) continue;
		son[p]++;
		dfs1(to,p);
	}
}

int dp[N];

// bool dfs(int p,int fa,int sum,int k)
// {
// 	sum-=son[p];
// 	sum+=k;
// 	if(sum<0) return 0;
// 	for(int to:E[p])
// 	{
// 		if(to==fa) continue;
// 		if(!dfs(to,p,sum,k)) return 0;
// 	}
// 	return 1;
// }

void dfs(int p,int fa,int k)
{
    int nw=son[p]-k;
    for(int to:E[p])
    {
        if(to==fa) continue;
        dfs(to,p,k);
        nw+=dp[to];
    }
    dp[p]=max(0ll,nw);
}

bool check(int k)
{
    memset(dp,0,sizeof(dp));
	// return dfs(1,0,0,k);
    dfs(1,0,k);
    return dp[1]<=0;
}

signed main()
{
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	#endif

	n=read();
	if(n==1) { cout<<"0"; return 0; }
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		E[u].push_back(v);
		E[v].push_back(u);
	}
	dfs1(1,0);

	int k=n;
	for(int i=19;i>=0;i--)
	{
		int to=k-(1<<i);
		if(to<=0) continue;
		if(check(to)) k=to;
	}
	cout<<k<<"\n";
	//mt19937_64 myrand(time(0));
	return 0;
}

19. 数据点分治

如果你看到一个题的数据范围过于奇怪,不要怀疑自己,可能他真的想让你数据点分治

P3646 [APIO2015] 巴厘岛的雕塑

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=5005;
int n,A,B;
int a[N];

void solve1()
{
	int dp[5005]={};
	// memset(dp,0x3f,sizeof(dp));
	// dp[0]=0;
	int tot=0;
	for(int k=40;k>=0;k--)
	{
		memset(dp,0x3f,sizeof(dp));
		dp[0]=0;
		for(int i=1;i<=n;i++)
		{
			int nw=0;
			for(int r=i;r>=1;r--)
			{
				nw+=a[r];
				if((((nw>>(k+1))<<(k+1))|tot)!=tot) continue;
				// if((nw>>k)&1) {cout<<"i="<<i<<" r="<<r<<"\n"; break;}
				if(!((nw>>k)&1))dp[i]=min(dp[i],dp[r-1]+1);
			}
		}
		if(dp[n]>B) tot|=(1ll<<k);
		// if(k<=3)
		// {
		// 	for(int i=1;i<=n;i++) cout<<dp[i]<<" ";
		// 	cout<<"\n\n";
		// }	
		//cout<<k<<" "<<dp[n]<<"\n";
	}
	write(tot);
	putchar('\n');
}

void solve2()
{
	bool dp[105][105];
	int tot=0;

	for(int k=40;k>=0;k--)
	{
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		for(int i=1;i<=n;i++)
		{
			int nw=0;
			for(int r=i;r>=1;r--)
			{
				nw+=a[r];
				if((((nw>>(k+1))<<(k+1))|tot)!=tot) continue;
				
				if(!((nw>>k)&1))
				{
					for(int j=0;j<min(r,B);j++)
					dp[i][j+1]|=dp[r-1][j];	
				}

				// if((nw>>k)&1) {cout<<"i="<<i<<" r="<<r<<"\n"; break;}
				// if(!((nw>>k)&1))dp[i]=min(dp[i],dp[r-1]+1);
			}
		}
		bool flag=0;
		for(int i=A;i<=B;i++) if(dp[n][i]) flag=1;
		if(!flag) tot|=1ll<<k;
	}
	write(tot);
}

signed main()
{
	// #ifndef ONLINE_JUDGE
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
	// #endif
	//mt19937_64 myrand(time(0));

	n=read();
	A=read();
	B=read();
	for(int i=1;i<=n;i++) a[i]=read();
	if(n>100)
	{
		solve1();
	}
	else
	{
		solve2();
	}

	return 0;
}

20. 排序将有限制(计数)问题的限制减弱

P3077 [USACO13FEB] Route Design G

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int N=1e5+5;
int n,m,r;
vector<int> E[N],G[N];
int dp[N];
int a1[N],a2[N];

signed main()
{
	n=read();
	m=read();
	r=read();
    for(int i=1;i<=n;i++) a1[i]=read();
    for(int i=1;i<=m;i++) a2[i]=read();
	for(int i=1;i<=r;i++)
	{
		int u=read(),v=read();
		E[u].push_back(v);
        G[v].push_back(u);
		// E[v].push_back(u);
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		sort(E[i].begin(),E[i].end());
		int maxn=0;
		for(int to:E[i])
		{
			int nw=dp[to];
			dp[to]=max(dp[to],maxn+a1[i]+a2[to]);
            // ans=max(ans,dp[to]);
			maxn=max(maxn,nw);
		}
        ans=max(ans,maxn+a1[i]);
        // for(int j=1;j<=m;j++) cout<<dp[j]<<" ";
        // cout<<"\n";
	}
	for(int i=1;i<=m;i++) ans=max(ans,dp[i]);

	memset(dp,0,sizeof(dp));
	for(int i=1;i<=m;i++)
	{
		sort(G[i].begin(),G[i].end());
		int maxn=0;
		for(int to:G[i])
		{
			int nw=dp[to];
			dp[to]=max(dp[to],maxn+a2[i]+a1[to]);
			maxn=max(maxn,nw);
		}
        ans=max(ans,maxn+a2[i]);
	}
	for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
	
	cout<<ans<<"\n";
	// #ifndef ONLINE_JUDGE
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
	// #endif
	//mt19937_64 myrand(time(0));
	return 0;
}

21. 移项思想。

把题目中的限制移项,你可能就有更优做法了。

P3089 [USACO13NOV] Pogo-Cow S

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int dp[1005][1005];
int maxn[1005][1005];
struct Node{
    int p,x;
}a[1<<20];
bool cmp(Node x,Node y) { return x.x<y.x; }
int n;

int query(int len,const int R)
{
    int l=1,r=R-1;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(a[R].x-a[mid].x>len) l=mid+1;
        else r=mid-1;
    }
    l-=4;
    r+=4;
    if(l<1) l=1;
    if(r>R-1) r=R-1;
    for(int i=r;i>=l;i--)
    if(a[R].x-a[i].x>=len) return i;
    return 0;
}

int ans;
void solve()
{
    memset(dp,0,sizeof(dp));
    memset(maxn,0,sizeof(maxn));
    for(int i=1;i<=n;i++)
    {
        dp[i][0]=a[i].p;
        maxn[i][0]=a[i].p;
        for(int j=1;j<i;j++)//上一个选 j
        {
            int len=a[i].x-a[j].x;
            int pos=query(len,j);

            // cout<<"i="<<i<<" j="<<j<<" len="<<len<<" pos="<<pos<<" dp["<<i<<"]["<<j<<"]=";

            dp[i][j]=max(dp[i][j],maxn[j][pos]+a[i].p);
            // cout<<dp[i][j]<<"\n";
            ans=max(ans,dp[i][j]);
            maxn[i][j]=max(maxn[i][j-1],dp[i][j]);
        }
        // maxn[i][i]=maxn[i][i-1];
    }
    // cout<<"\n---------------------------------------\n\n";
}

signed main()
{
    n=read();
    a[0].x=1e6-1;
    for(int i=1;i<=n;i++)
    {
        a[i].x=read();
        a[i].p=read();
        ans=max(ans,a[i].p);
    }
    sort(a+1,a+1+n,cmp);

    solve();
    for(int i=1;i<=n;i++)
    {
        a[i].x=1e6+1-a[i].x;
    }
    sort(a+1,a+1+n,cmp);

    // for(int i=1;i<=n;i++)
    // {
    //     cout<<"i="<<i<<" a[i].x="<<1e6+1-a[i].x<<" a[i].p="<<a[i].p<<"\n";
    // }


    solve();

    cout<<ans;

	//mt19937_64 myrand(time(0));
	return 0;
}

//4 -> 5 -> 7 -> 10 
/*

! i=1 a[i].x=10 a[i].p=5
i=2 a[i].x=8 a[i].p=10
! i=3 a[i].x=7 a[i].p=6
! i=4 a[i].x=5 a[i].p=6
! i=5 a[i].x=4 a[i].p=8
i=6 a[i].x=1 a[i].p=1

*/

22. 临项交换贪心

P3076 [USACO13FEB] Taxi G

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int ans;
int x[1<<20],y[1<<20];
int n;

signed main()
{
    n=read();
    int m=read();
    for(int i=1;i<=n;i++)
    {
        x[i]=read();
        y[i]=read();
        ans+=abs(y[i]-x[i]);
    }
    // cout<<ans<<"\n";
    n++;
    y[n]=0;
    x[n]=m;
    sort(x+1,x+1+n);
    sort(y+1,y+1+n);
    for(int i=1;i<=n;i++) ans+=abs(x[i]-y[i]);
    cout<<ans<<"\n";
	// #ifndef ONLINE_JUDGE
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
	// #endif
	//mt19937_64 myrand(time(0));
	return 0;
}

23. 均分纸牌问题

两种做法,

  • 推柿子得绝对值不等式
  • 三分法求函数极值

P3051 [USACO12MAR] Haybale Restacking G

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std; 

int n,a[1<<20],ans,b[1<<20],sum[1<<20];

signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i]>>b[i],sum[i]=a[i-1]-b[i-1]+sum[i-1];
	sum[1]=a[n]-b[n]+sum[n];
	sort(sum+1,sum+1+n);
	for(int i=1;i<=n;i++)
	{
		ans+=abs(sum[i]-sum[n/2+1]);
	}
	
	cout<<ans;
	return 0;
}

24. dp 转移画出转移路径,网格图再求解

AT_abc279_g [ABC279G] At Most 2 Colors my sol

P2516 [HAOI2010] 最长公共子序列

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

string s1,s2;
const int N= 5005,mod=1e8;
short dp[2][N];
signed g[N][N];

signed main()
{
    // cin<<s1<<s2;
    cin>>s1>>s2;
    s1=' '+s1;
    s2='#'+s2;
    // dp[0][0]=1;
    // g[1][0]=1;
    for(int i=0;i<N;i++) g[i][0]=g[0][i]=1;
    for(int i=1;i<s1.size()-1;i++)
    {
        memset(dp[i&1],0,sizeof(dp[i&1]));
        for(int j=1;j<s2.size()-1;j++)
        {
            if(s1[i]==s2[j])
            {
                dp[i&1][j]=dp[(i-1)&1][j-1]+1;
                g[i][j]+=g[i-1][j-1];
            }
            else dp[i&1][j]=max(dp[i&1][j-1],dp[(i-1)&1][j]);
            // if(dp[i][j]==dp[i-1][j]&&dp[i][j]==dp[i][j-1]) g[i][j]+=g[i-1][j]+g[i][j-1]-g[i-1][j-1];
            if(dp[i&1][j]==dp[(i-1)&1][j]) g[i][j]+=g[i-1][j];
            if(dp[i&1][j]==dp[i&1][j-1]) g[i][j]+=g[i][j-1];
            if(s1[i]!=s2[j]&&dp[(i-1)&1][j-1]==dp[i&1][j]) g[i][j]-=g[i-1][j-1];
            g[i][j]%=mod;
        }        
    }

    cout<<dp[(s1.size()-2)&1][s2.size()-2]<<"\n"<<(g[s1.size()-2][s2.size()-2]+mod)%mod<<"\n";
    
	// #ifndef ONLINE_JUDGE
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
	// #endif
	// //mt19937_64 myrand(time(0));
	return 0;
}

25. dp[x][y] 两维限制转化为 dp[x] 一维限制,dp[x] 记录 可行的 y 的最小值

设一维 dp 状态,所记录的值是原先二维 dp 数组的第二维的最小值。

可以优化很多。

P2224 [HNOI2001] 产品加工

点击查看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read() {
	int res=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9') {res=res*10+ch-'0'; ch=getchar();}
	return res*f;
}
const int MAXN=6005;
const int INF=998244353;
int n,sum,last,now;
int t1[MAXN],t2[MAXN],t3[MAXN];
int f[MAXN*5];
signed main() {
 	for(register int i=1;i<=MAXN*5;++i)
 		f[i]=INF;
 	f[0]=0;
	n=read();
	for(register int i=1;i<=n;i++) {
		t1[i]=read(); t2[i]=read(); t3[i]=read();
		sum+=max(t1[i],max(t2[i],t3[i]));
		for(register int j=sum;j>=0;--j) {
			int a=INF,b=INF,c=INF;
			if(t1[i]&&j>=t1[i]) a=f[j-t1[i]];
			if(t3[i]&&j>=t3[i]) c=f[j-t3[i]]+t3[i];
			if(t2[i]) b=f[j]+t2[i];
			f[j]=min(min(a,b),c);
		}
 	}
 	int mx=INF;
 	for(register int i=0;i<=sum;++i)
 		mx=min(mx,max(i,f[i]));
 	printf("%d\n",mx);
	return 0;
}

26. 分块打表

P1662 数7

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;

int pd(int x)
{
	if(x%7==0) return 1;
	while(x)
	{
		if(x%10==7) return 1;
		x/=10;
	}
	return 0;
}

int n,x,op;
int a[1010][2]={0,1,469,-1,139,1,1086,-1,768,1,860,-1,1200,1,837,1,837,-1,507,1,117,-1,1136,1,1228,-1,231,1,1205,1,337,-1,7,1,954,1,954,-1,862,1,522,-1,885,-1,416,1,746,-1,1136,1,117,-1,25,1,1022,1,1022,1,154,-1,1161,1,771,-1,453,1,545,-1,885,1,522,1,991,-1,661,-1,661,1,979,-1,887,1,547,-1,910,-1,441,1,771,-1,1161,1,142,-1,50,-1,50,1,1024,1,156,-1,1163,1,773,-1,455,1,547,-1,887,1,524,1,993,1,993,-1,46,1,364,-1,272,1,1269,-1,295,-1,1163,1,156,-1,546,1,864,1,864,-1,1204,1,841,1,841,1,841,1,841,1,841,1,841,1,841,1,841,1,841,1,841,1,841,-1,523,1,615,-1,955,1,592,1,1061,-1,731,1,341,1,341,-1,249,1,1246,-1,272,-1,1140,1,133,-1,523,1,841,-1,749,1,409,1,409,1,878,-1,548,1,158,-1,1177,1,1269,-1,272,1,1246,1,378,-1,48,-1,48,1,366,-1,274,1,1271,-1,297,-1,1165,1,158,-1,548,1,866,-1,774,-1,774,1,411,1,880,-1,550,1,160,-1,1179,1,1271,-1,274,1,1248,1,380,1,380,-1,770,1,1088,-1,996,1,656,-1,1019,-1,550,1,880,-1,1270,1,251,1,251,-1,591,1,228,1,697,-1,367,1,1314,-1,996,1,1088,-1,91,1,1065,1,1065,-1,735,1,345,-1,27,1,119,-1,459,1,96,1,565,-1,235,1,1182,1,1182,-1,1090,1,750,-1,1113,-1,644,1,974,-1,27,1,345,-1,253,1,1250,1,1250,1,382,-1,52,-1,52,-1,52,-1,52,-1,52,-1,52,-1,52,-1,52,-1,52,-1,52,-1,52,1,1049,-1,75,-1,943,1,1273,-1,326,1,644,-1,552,-1,552,1,189,1,658,-1,328,1,1275,-1,957,1,1049,-1,52,1,1026,1,158,1,158,-1,548,1,866,-1,774,1,434,-1,797,-1,328,1,658,-1,1048,1,29,1,29,-1,369,1,6,1,475,-1,145,1,1092,-1,774,1,866,-1,1206,1,843,1,843,-1,513,1,123,-1,1142,1,1234,-1,237,1,1211,1,343,-1,13,1,960,1,960,-1,868,1,528,-1,891,-1,422,1,752,-1,1142,1,123,-1,31,1,1028,1,1028,1,160,-1,1167,1,777,-1,459,1,551,-1,891,1,528,1,997,-1,667,-1,667,1,985,-1,893,1,553,-1,916,-1,447,1,777,-1,1167,1,148,-1,56,-1,56,1,1030,1,162,-1,1169,1,779,-1,461,1,553,-1,893,1,530,1,999,1,999,-1,52,1,370,1,370,1,370,1,370,1,370,1,370,1,370,1,370,1,370,1,370,1,370,1,839,-1,509,1,119,-1,1138,1,1230,-1,233,1,1207,1,1207,-1,877,1,487,-1,169,1,261,-1,601,1,238,1,707,-1,377,1,1324,1,1324,-1,1232,1,892,-1,1255,-1,786,1,1116,-1,169,1,487,-1,395,1,55,1,55,1,524,-1,194,1,1141,-1,823,1,915,-1,1255,1,892,1,24,-1,1031,-1,1031,1,12,-1,1257,1,917,-1,1280,-1,811,1,1141,-1,194,1,512,-1,420,-1,420,1,57,1,526,-1,196,1,1143,-1,825,1,917,-1,1257,1,894,1,26,1,26,-1,416,1,734,-1,642,1,302,-1,665,-1,196,1,526,-1,916,1,1234,1,1234,-1,237,1,1211,1,343,-1,13,1,960,-1,642,1,734,-1,1074,1,711,1,711,-1,381,1,1328,-1,1010,1,1102,-1,105,1,1079,1,211,-1,1218,1,828,1,828,-1,736,1,396,1,396,1,396,1,396,1,396,1,396,1,396,1,396,1,396,1,396,1,396,-1,786,1,1104,-1,1012,1,672,-1,1035,-1,566,1,896,1,896,-1,578,1,670,-1,1010,1,647,1,1116,-1,786,1,396,-1,78,1,170,1,170,-1,533,-1,64,1,394,-1,784,1,1102,-1,1010,1,670,-1,1033,-1,564,-1,564,1,174,-1,1193,1,1285,-1,288,1,1262,1,394,-1,64,1,1011,-1,693,-1,693,1,353,-1,716,-1,247,1,577,-1,967,1,1285,-1,1193,1,853,-1,1216,-1,1216,1,209,-1,599,1,917,-1,825,1,485,-1,848,-1,379,1,709,-1,1099,-1,1099,1,1191,-1,194,1,1168,1,300,-1,1307,1,917,-1,599,1,691,-1,1031,-1,1031,-1,562,1,892,-1,1282,1,263,-1,171,1,1168,-1,194,-1,1062,1,55,1,55,-1,1074,1,1166,-1,169,1,1143,1,275,-1,1282,1,892,-1,574,1,666,1,666,-1,1029,-1,560,-1,560,-1,560,-1,560,-1,560,-1,560,-1,560,-1,560,-1,560,-1,560,-1,560,1,652,-1,992,1,629,1,1098,-1,768,1,378,-1,60,-1,60,1,1057,-1,83,-1,951,1,1281,-1,334,1,652,-1,560,1,220,-1,583,-1,583,1,913,-1,1303,1,284,-1,192,1,1189,-1,215,-1,1083,1,76,-1,466,-1,466,1,558,-1,898,1,535,1,1004,-1,674,1,284,-1,1303,1,58,-1,398,-1,398,-1,1266,1,259,-1,649,1,967,-1,875,1,535,-1,898,-1,429,1,759,1,759,-1,441,1,533,-1,873,1,510,1,979,-1,649,1,259,-1,1278,1,33,1,33,-1,396,-1,1264,1,257,-1,647,1,965,-1,873,1,533,-1,896,-1,427,-1,427,1,37,-1,1056,1,1148,-1,151,1,1125,1,257,-1,1264,1,874,-1,556,-1,556,1,216,-1,579,-1,110,1,440,-1,830,1,1148,-1,1056,1,716,-1,1079,-1,1079,1,72,-1,462,-1,462,-1,462,-1,462,-1,462,-1,462,-1,462,-1,462,-1,462,-1,462,-1,462,1,99,1,568,-1,238,1,1185,-1,867,1,959,-1,1299,-1,1299,-1,830,1,1160,-1,213,1,531,-1,439,1,99,-1,462,-1,1330,1,323,1,323,-1,5,1,97,-1,437,1,74,1,543,-1,213,1,1160,-1,842,1,934,1,934,-1,1297,-1,828,1,1158,-1,211,1,529,-1,437,1,97,-1,460,-1,1328,-1,1328,1,938,-1,620,1,712,-1,1052,1,689,1,1158,-1,828,1,438,-1,120,-1,120,1,1117,-1,143,-1,1011,1,4,-1,394,1,712,-1,620,1,280,-1,643,-1,643,1,973,-1,26,1,344,-1,252,1,1249,-1,275,-1,1143,1,136,-1,526,-1,526,1,618,-1,958,1,595,1,1064,-1,734,1,344,-1,26,1,118,-1,458,-1,458,-1,1326,1,319,-1,709,1,1027,-1,935,1,595,-1,958,-1,489,1,819,1,819,-1,501,1,593,1,593,1,593,1,593,1,593,1,593,1,593,1,593,1,593,1,593,1,593,-1,263,1,1210,-1,892,1,984,-1,1324,1,961,1,93,1,93,-1,483,1,801,-1,709,1,369,-1,732,-1,263,1,593,-1,983,1,1301,1,1301,-1,304,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,1,1278,-1,331,1,649,-1,557,1,217,-1,580,-1,111,1,441,1,441,-1,123,1,215,-1,555,1,192,1,661,-1,331,1,1278,-1,960,1,1052,1,1052,-1,78,-1,946,1,1276,-1,329,1,647,-1,555,1,215,-1,578,-1,109,-1,109,1,1056,-1,738,1,830,-1,1170,1,807,1,1276,-1,946,1,556,-1,238,-1,238,1,1235,-1,261,-1,1129,1,122,-1,512,1,830,-1,738,1,398,-1,761,-1,761,1,1091,-1,144,1,462,-1,370,1,30,-1,393,-1,1261,1,254,-1,644,-1,644,1,736,-1,1076,1,713,1,1182,-1,852,1,462,-1,144,1,236,-1,576,-1,576,-1,107,1,437,1,437,1,437,1,437,1,437,1,437,1,437,1,437,1,437,1,437,1,437,-1,777,1,414,1,883,-1,553,1,163,-1,1182,1,1274,1,1274,-1,300,-1,1168,1,161,-1,551,1,869,-1,777,1,437,-1,800,-1,331,-1,331,1,1278,-1,960,1,1052,-1,55,1,1029,1,161,-1,1168,1,778,-1,460,-1,460,1,120,-1,483,-1,14,1,344,-1,734,1,1052,-1,960,1,620,-1,983,-1,983,1,1313,-1,366,1,684,-1,592,1,252,-1,615,-1,146,1,476,-1,866,-1,866,1,958,-1,1298,1,935,1,67,-1,1074,1,684,-1,366,1,458,-1,798,-1,798,-1,329,1,659,-1,1049,1,30,-1,1275,1,935,-1,1298,-1,829,1,1159,1,1159,-1,841,1,933,-1,1273,1,910,1,42,-1,1049,1,659,-1,341,1,433,1,433,-1,796,-1,327,1,657,-1,1047,1,28,-1,1273,1,933,-1,1296,-1,827,-1,827,1,437,-1,119,-1,119,-1,119,-1,119,-1,119,-1,119,-1,119,-1,119,-1,119,-1,119,-1,119,-1,987,1,1317,-1,370,1,688,-1,596,1,256,-1,619,-1,619,1,949,-1,2,1,320,-1,228,1,1225,-1,251,-1,1119,1,112,-1,502,-1,502,1,594,-1,934,1};

signed main()
{
	cin>>n;
	x=a[n/1000000][0];
	op=a[n/1000000][1];
	
	for(int i=n-n%1000000+1;i<=n;i++)
	{
		x+=op;
		if(x==1338) x=1;
		if(x==0) x=1337;
		if(pd(i)) op=-op;
	}
	
	cout<<x;
	return 0;
}

27. 正着做很难做考虑反着做(容斥/二反)

P1450 [HAOI2008] 硬币购物

点击查看代码
#include<stdio.h>
// #define int long long

// using namespace std;

// const int Size=(1<<20)+1;
// char buf[Size],*p1=buf,*p2=buf;
// char buffer[Size];
// int op1=-1;
// const int op2=Size-1;
// #define getchar()                                                              \
// (tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
// 	? EOF                                                                 \
// 	: *ss++)
// char In[1<<20],*ss=In,*tt=In;

int c1,c2,c3,c4,i,d1,d2,d3,d4,s,nw1,nw2,nw3,nw4;
long long dp[100005],ans;
int n;
const int N=1e5;
signed main()
{
    scanf("%d%d%d%d%d",&c1,&c2,&c3,&c4,&n);

    dp[0]=1;
    for(i=c1;i<=N;i++) dp[i]+=dp[i-c1];
    for(i=c2;i<=N;i++) dp[i]+=dp[i-c2];
    for(i=c3;i<=N;i++) dp[i]+=dp[i-c3];
    for(i=c4;i<=N;i++) dp[i]+=dp[i-c4];
    for(i=1;i<=n;i++)
    {
            // d1=read(),d2=read(),d3=read(),d4=read(),s=read();
            scanf("%d%d%d%d%d",&d1,&d2,&d3,&d4,&s);
            ans=dp[s];
            nw1=s-(d1+1)*c1,nw2=s-(d2+1)*c2,nw3=s-(d3+1)*c3,nw4=s-(d4+1)*c4;
            if(nw1>=0) ans-=dp[nw1];
            if(nw2>=0) ans-=dp[nw2];
            if(nw3>=0) ans-=dp[nw3];
            if(nw4>=0) ans-=dp[nw4];
            nw1=s-(d1+1)*c1-(d2+1)*c2;
            nw2=s-(d1+1)*c1-(d3+1)*c3;
            nw3=s-(d1+1)*c1-(d4+1)*c4;
            if(nw1>=0) ans+=dp[nw1];
            if(nw2>=0) ans+=dp[nw2];
            if(nw3>=0) ans+=dp[nw3];
            
            nw1=s-(d2+1)*c2-(d3+1)*c3;
            nw2=s-(d2+1)*c2-(d4+1)*c4;
            nw3=s-(d3+1)*c3-(d4+1)*c4;
            if(nw1>=0) ans+=dp[nw1];
            if(nw2>=0) ans+=dp[nw2];
            if(nw3>=0) ans+=dp[nw3];
        
            nw1=s-(d1+1)*c1-(d2+1)*c2-(d3+1)*c3;
            nw2=s-(d1+1)*c1-(d2+1)*c2-(d4+1)*c4;
            nw3=s-(d1+1)*c1-(d3+1)*c3-(d4+1)*c4;
            nw4=s-(d2+1)*c2-(d3+1)*c3-(d4+1)*c4;
        
            if(nw1>=0) ans-=dp[nw1];
            if(nw2>=0) ans-=dp[nw2];
            if(nw3>=0) ans-=dp[nw3];
            if(nw4>=0) ans-=dp[nw4];
        
            nw1=s-(d1+1)*c1-(d2+1)*c2-(d3+1)*c3-(d4+1)*c4;
        
            if(nw1>=0) ans+=dp[nw1];
            printf("%lld\n",ans);
            // return ans;
            // write(ans);
            // putchar('\n');
    }
	//mt19937_64 myrand(time(0));
	return 0;
}

28. 在线决策单调性可以单 \(\log\) 分治做。

https://www.luogu.com.cn/article/vqf42hah


posted @ 2025-11-25 10:15  Wy_x  阅读(81)  评论(1)    收藏  举报