丐版 OI 技巧 / 杂项部分总结 + 作者学习笔记
合作:
SKK 部分:【前面忘了】,SKK,我【中间忘了】的信仰,我【中间忘了】的希望:https://www.cnblogs.com/S-Keep-Kiding/p/19267094
Wy_x 部分:https://www.cnblogs.com/Wy-x/p/19265940
我写的的学习笔记部分:
3. 树上依赖性背包 学习笔记 | P6326 Shopping 题解
Tricks:收录不常见模板题,经典好题/思维题/trick 题(也有一些神秘题)。CF/AT 后面可能会补。
1. 可持久化区间修改区间查询线段树:
点击查看代码
#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
一般用高斯消元 \(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. 折半报警器。
假设这个报警器要触发报警还需要 \(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. 扫描线 + 并查集维护值域连续段。
过于高妙,我也不会讲。
点击查看代码
#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. 分治优化区间背包问题
先考虑不跨过分治中点的贡献,递归回来后再考虑跨过分治中点的贡献,然后合并背包或者暴力跑背包。
更像猫树的写法
#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
适用于转移是卷积的形式。
点击查看代码
#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 深度求和问题
可以转化为根链上每个点加 \(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. 绝对众数问题:摩尔投票法
基本思想:
注意到这样一个现象:在任何数组中,出现次数大于该数组长度一半的值只能有一个。
摩尔投票法的基本思想很简单,在每一轮投票过程中,从数组中找出一对不同的元素,将其从数组中删除。
这样不断的删除直到无法再进行投票,如果数组为空,则没有任何元素出现的次数超过该数组长度的一半。如果只存在一种元素,那么这个元素则可能为目标元素。
我们可以用线段树来维护这个过程。注意左右儿子剩的元素相同时的 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. 广义(?)矩阵树定理
点击查看代码
#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。
点击查看代码
#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\) 个的矩阵优化的解题方式
思路是在答案矩阵后再拼接一个答案矩阵,最后查询时求矩阵中一段元素的和即可。
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. 数据点分治
如果你看到一个题的数据范围过于奇怪,不要怀疑自己,可能他真的想让你数据点分治
点击查看代码
#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. 移项思想。
把题目中的限制移项,你可能就有更优做法了。
点击查看代码
#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. 临项交换贪心
点击查看代码
#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
点击查看代码
#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 数组的第二维的最小值。
可以优化很多。
点击查看代码
#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. 分块打表
点击查看代码
#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. 正着做很难做考虑反着做(容斥/二反)
点击查看代码
#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
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/p/19265940
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号