远古离散题
作者:互联网
前言
因为之前做法假了或者代码写太丑了,还不如重写一遍。
之前CSDN过河的代码会被洛谷的HACK数据HACK掉QWQ。
过河
题目链接:P1052
题目大意
有一条河,起点为 \(0\),终点为 \(L(L\leq 10^9)\),其中一些位置有石头。
有一只青蛙一次可以跳 \([l,r](r\leq 10)\) 的距离,如果它想从起点跳到 \(\geq L\) 的点,至少需要越过多少个石头。
思路
有一个很简单的方法,设 \(dp[i]\) 表示跳到 \(i\) 时至少需要越过多少个石头,
那么 \(\large dp[i]=\min_{j=l}^r\{dp[i-j]\}+stone[i]\),其中 \(stone[i]\) 表示第 \(i\) 个点是否为石头。
最后的答案就是 \(\large \min_{j=0}^{r-1}dp[L+j]\),这样时间复杂度为 \(O(r(L+r))\),主要是要将 \(L\) 的复杂度变小。
考虑将石头的位置离散,并且只要跳过所有的石头就可以随便跳,所以答案只与石头的位置有关。
可以发现不小于 \(72\) 的步数都能被表示出来,那么只要步数超过 \(72\) 就可以缩成 \(72\)。
具体可以沿用小凯的疑惑的结论,不能表示的最大数为 \(r*(r-1)-r-(r-1)\)。
那么这样 \(L\) 就被缩小成了 \(O(mr^2)\) 量级,那么最后的时间复杂度就是 \(O(mr^3)\)
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N=111,lim=72; int ans=0x3f3f3f3f;
int L,l,r,n,a[N],stone[N*72],dp[N*72];
int main(){
ios::sync_with_stdio(0);
cin>>L>>l>>r>>n;
for (int i=1;i<=n;++i) cin>>a[i];
sort(a+1,a+1+n);//排序
if (l==r){//如果 l=r 要特判
ans=0;
for (int i=1;i<=n;++i)
if (a[i]%l==0) ++ans;
cout<<ans;
return 0;
}
for (int i=1,lst=0;i<=n;++i){
int now=min(a[i]-lst,lim);//石头间距离超过72可以缩成72
lst=a[i],a[i]=a[i-1]+now;
}
for (int i=1;i<=n;++i) stone[a[i]]=1;
for (int i=1;i<a[n]+r;++i){
dp[i]=0x3f3f3f3f;
for (int j=l;j<=r;++j)//dp[i]=min{dp[i-j]}+1
if (i>=j)
dp[i]=min(dp[i],dp[i-j]);
dp[i]+=stone[i];
}
for (int i=a[n];i<a[n]+r;++i) ans=min(ans,dp[i]);
cout<<ans;
return 0;
}
程序自动分析
题目链接:P1955
题目大意
多组数据,有若干个变量,\(n\) 条限制,形如 \(x_i\neq x_j\) 或者 \(x_i=x_j\),问这些变量是否能满足所有限制。
\(n\leq 10^6,i,j\leq 10^9\)
思路
先考虑 \(x_i=x_j\) 的限制,再考虑 \(x_i\neq x_j\) 的限制,按照 \(x_i=x_j\) 的限制将 \(i,j\) 缩成一个连通块,
那么实际上只需要在 \(x_i\neq x_j\) 的时候判断 \(i,j\) 是否不在同一连通块,如果在同一连通块就无解。
然而 \(i,j\) 的范围比较大,不过实际上可用的 \(i,j\) 不超过 \(2n\) 个,那么直接将编号离散即可。
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1000011;
int x[N],y[N],opt[N],b[N<<1],rk[N],n,Test,flag,f[N],m;
bool cmp(int x,int y){return opt[x]>opt[y];}//把xi=xj的限制放在前面
int find_root(int u){//并查集
if (f[u]==u) return u;
return f[u]=find_root(f[u]);
}
int main(){
ios::sync_with_stdio(0);
for (cin>>Test;Test;--Test){
cin>>n,flag=0;
for (int i=1;i<=n;++i){
cin>>x[i]>>y[i]>>opt[i];
b[i]=x[i],b[i+n]=y[i],rk[i]=i;
}
sort(b+1,b+1+n*2),sort(rk+1,rk+1+n,cmp);
m=unique(b+1,b+1+n*2)-b-1;//将xi,xj离散
for (int i=1;i<=m;++i) f[i]=i;
for (int i=1;i<=n;++i){
x[rk[i]]=lower_bound(b+1,b+1+m,x[rk[i]])-b;
y[rk[i]]=lower_bound(b+1,b+1+m,y[rk[i]])-b;
int fx=find_root(x[rk[i]]),fy=find_root(y[rk[i]]);
if (opt[rk[i]]&&fx!=fy) f[fx]=fy;//如果xi=xj且当前不处于同一连通块就连起来
else if (!opt[rk[i]]&&fx==fy) {flag=1; break;}
//如果xi不等于xj且在同一连通块则无解
}
if (flag) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
Parity Game
题目链接:P5937
题目大意
有一个长度为 \(n\) 的 \(01\) 串 \(a\),现在有 \(m\) 条限制形如 \(a[l\sim r]\) 中有奇数个 \(1\) 或者偶数个 \(1\),
是否存在第 \(i\) 条限制不符合要求,并且前 \(i-1\) 条限制均能符合要求。
\(n\leq 10^9,m\leq 5*10^3\)
思路
限制其实就是 \(s[r]-s[l-1]\) 的奇偶性,可以考虑边带权并查集,处理出每个位置到并查集根的奇偶性 \(s'[x]\),那么每次合并的时候实际上 \(x\) 的根节点到 \(y\) 的根节点的奇偶性就是 \(s'[x]\) \(xor\) \(s'[y]\) \(xor\) \(z\)。
然后路径压缩的时候同样维护到根节点的奇偶性,但是 \(n\) 太大,考虑可用的位置不超过 \(2m\) 个,直接离散即可。
代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const int N=10011; string S;
int n,m,b[N],x[N],y[N],z[N],f[N],s[N];
int find_root(int u){
if (f[u]==u) return u;
int U=find_root(f[u]);
s[u]^=s[f[u]];//原来s[u]是到f[u]的奇偶性,异或s[f[u]]后是到根节点的奇偶性
return f[u]=U;
}
int main(){
ios::sync_with_stdio(0);
cin>>n>>m;
for (int i=1;i<=m;++i){
cin>>x[i]>>y[i]>>S;
b[i]=--x[i],b[i+m]=y[i];
if (S[0]=='o') z[i]=1;
}
sort(b+1,b+1+m*2),n=unique(b+1,b+1+m*2)-b-1;//离散
for (int i=1;i<=n;++i) f[i]=i;
for (int i=1;i<=m;++i){
x[i]=lower_bound(b+1,b+1+n,x[i])-b;
y[i]=lower_bound(b+1,b+1+n,y[i])-b;
int fx=find_root(x[i]),fy=find_root(y[i]);
if (fx==fy){
if ((s[x[i]]^s[y[i]]^z[i])&1){
//由于异或的自反性,s[x[i]]^s[y[i]]得到的是x[i]到y[i]的奇偶性,必须与z[i]相同
cout<<i-1;
return 0;
}
}else f[fx]=fy,s[fx]=s[x[i]]^s[y[i]]^z[i];//fx到fy的奇偶性
}
cout<<m;
return 0;
}
标签:10,int,奇偶性,离散,leq,远古,include,dp 来源: https://www.cnblogs.com/Spare-No-Effort/p/16065896.html