合作:
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 longusing 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 longusing namespace std;// #ifndef ONLINE_JUDGE
// #define ONLINE_JUDGE
// #endifconst 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_JUDGEios::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);// #endifn=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
// #endifconst 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_JUDGEfreopen("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 longusing 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_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifk=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";} }//1881cout<<(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 longusing 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 longusing 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 longusing 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 doubleconst 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 longusing 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 longusing 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_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifint 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 longusing 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_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifn=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 longusing 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 longusing 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 longusing 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 longusing 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 longusing 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;
}