题目比较神仙,注意是题目神仙
贪婪暗示贪心,堆积暗示堆优化$\%\%\%\%\%\%\%$
两个乱搞$+$一个堆优化$dp$
嚎叫响彻在贪婪的机房
题解
对于一个序列来说只要他们差的$gcd$不为$1$就可以构成等差数列
例如
$2$ $4$ $16$
$2$与$4$差$2$ $4$与$16$差$12$
$gcd(2,12)!=1$故构成等差序列
那么我们维护公差,然后每次的差和当前公差比较,若$gcd==1$则等差数列从这里断开,否则将公差置成$gcd$
举个例子
$2$ $8$ $14$ $16$ $18$ $20$
$2$ $8$ $14$构成公差为$6$等差数列,之后$16$ $18$ $20$构成公差为$2$等差序列
显然我们可以让他们合并为公差为$2$等差序列
注意判重,判差为$1$,
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 1111111 ll read(){ ll x=0,f=1;char c=getchar(); while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); } while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } return x*f; } set<ll> st; set<ll> ::iterator it; ll n,m,d=0,ans=0; ll a[A]; void cl(ll x){ st.clear(); d=0; ans++; st.insert(a[x]); } ll gcd(ll x,ll y){ if(y==0) return x; return gcd(y,x%y); } int main(){ n=read(); for(ll i=1;i<=n;i++){ a[i]=read(); } cl(1); for(ll i=2;i<=n;i++){ if((it=st.find(a[i]))!=st.end()) { // printf("i=-%lld ",i); cl(i); continue; } if(abs(a[i]-a[i-1])==1||a[i]==a[i-1]){ cl(i); continue; } // printf("d=%lld ",d); if(d==0){ st.insert(a[i]); d=abs(a[i]-a[i-1]); } else { ll g=gcd(d,abs(a[i]-a[i-1])); // printf("g=%lld d=%lld abs=%lld ",g,d,abs(a[i]-a[i-1])); if(g==1||g==0){ cl(i); } else { st.insert(a[i]); d=g; } } } printf("%lld ",ans); }
主仆见证了 Hobo 的离别
题解
建边,建立包含关系的树,例如$1,2$交集为$3$那么$1$包含$3$,$2$包含$3$
再例如$1,2$并集为$3$那么$3$包含$1$,$3$包含$2$
询问$x$,$y$所属关系就从$y$开始$dfs$若找到$x$即符合
暴力就是正解
让我们分析一下复杂度
题目中说
新元件的编号等于融合之前元件的总个数加一。当然,参与融合的 K个元件融合之后依然存在,并且每个元件至多参与一次融合。
数据范围
极限情况下肯定就是分开连边
那么$250000$个分开连边,产生$125000$,然后下一层产生$62500$再下一层.....
显然是$log$的最终是$18$层
而且我们建出来树是下图这样的从一点往下搜
所以极限复杂度$18*250000$可过
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 1111111 ll read(){ ll x=0,f=1;char c=getchar(); while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); } while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } return x*f; } ll n,m,cnt,ok,tot=0; ll some[A],fa[A],nxt[A],ver[A],head[A]; ll find(ll x){ if(fa[x]!=x){ fa[x]=find(fa[x]); } return fa[x]; } void haha(){ for(ll i=1;i<=500000;i++){ fa[i]=i; } } void merge(ll x,ll y){ x=find(x),y=find(y); if(x!=y) fa[x]=y; } void add(ll x,ll y){ // printf("x=%lld y=%lld ",x,y); nxt[++tot]=head[x],head[x]=tot,ver[tot]=y; } void dfs(ll x,ll pre,ll ineed){ if(x==ineed) ok=1; if(ok) return ; for(ll i=head[x];i;i=nxt[i]){ ll y=ver[i]; if(y==pre) continue; dfs(y,x,ineed); } } /*ll ithave[A],beihave[A]; ll getithave(ll x){ if(ithave[x]!=x){ ithave[x]=getithave(ithave[x]); } return ithave[x]; } void mergeit(ll x,ll y){ x=getithave(x); y=getithave(y); if(ithave[x]!=ithave[y]){ ithave[x]=y; } } ll getbeihave(ll x){ if(beihave[x]!=x){ beihave[x]=getbeihave(beihave[x]); } return beihave[x]; } void mergebei(ll x,ll y){ x=getbeihave(x); y=getbeihave(y); if(beihave[x]!=beihave[y]){ beihave[x]=y; } }*/ int main(){ n=read(),m=read(); cnt=n; for(ll i=1,opt,ques,k,QwQ;i<=m;i++){ ques=read(); if(ques==0){ opt=read(),k=read(); cnt++; if(opt==1){ for(ll j=1;j<=k;j++){ QwQ=read(); //1属于2 1--->2 add(cnt,QwQ); if(k==1) add(QwQ,cnt); } } else { for(ll j=1;j<=k;j++){ QwQ=read(); add(QwQ,cnt); if(k==1) add(cnt,QwQ); } } } else{ ok=0; ques=read(),QwQ=read(); dfs(QwQ,0,ques); printf("%lld ",ok); } } }
征途堆积出友情的永恒
题解
首先普通dp应该都会吧
$f[i]=min(f[j]+max(sum[i]-sum[j],b[j]))$
for(ll i=1;i<=n;i++){ for(ll j=max(i-k,0ll);j<=i-1;j++){ ll fee=max(sum[i]-sum[j],a[j]); f[i]=min(f[j]+fee,f[i]); } }
怎么优化,
线段树或者堆
思考sum[i]变化很烦,线段树很难维护(然而Mr_zkt维护出来了$%%%$)我没打线段树
用堆维护我们需要寻找不变量显然$f[j]+b[j]$和$f[j]-sum[j]$是不变量
开两个小根堆,一个维护$min(f[j]+b[j])$一个维护$f[j]-sum[j]$
转移时$min(q1.top(),q2.top+sum[i])$
细节稍多
- $STL$的各种$empty$往上仍,
- 判断是否可以转移(<=k)限制
- 在$f[j]+b[j]$$<$$f[j]-sum[j]+sum[i]$时第一个堆不合法,第一个堆扔到第二个堆
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 1010101 ll f[A],a[A],sum[A],b[A]; ll n,k,minn; struct node{ ll id,val; friend bool operator < (const node &a,const node &b){ return a.val>b.val; } }; priority_queue<node> q1,q2; //q1用来存f+b q2存 f-s ll read(){ ll x=0,f=1;char c=getchar(); while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); } while(isdigit(c)){ x=x*10+(c-'0'); c=getchar(); } return f*x; } void work(){ memset(f,0x3f,sizeof(f)); f[0]=0; node o; o.id=0,o.val=b[0]; q1.push(o); for(ll i=1;i<=n;i++){ node x1,x2; while(!q1.empty()){ x1=q1.top(); if(x1.id>=i-k) break; q1.pop(); } while(!q2.empty()){ x2=q2.top(); if(x2.id>=i-k) break; q2.pop(); } while(!q1.empty()){ x1=q1.top(); if(x1.val>=f[x1.id]-sum[x1.id]+sum[i]) break; // printf("feifa "); // printf("x1.val=%lld id=%lld f-s=%lld ",x1.val,x1.id,f[x1.id]-sum[x1.id]+sum[i]); q1.pop(); node x3; x3.id=x1.id,x3.val=f[x1.id]-sum[x1.id]; q2.push(x3); } while(!q1.empty()){ x1=q1.top(); if(x1.id>=i-k) break; q1.pop(); } while(!q2.empty()){ x2=q2.top(); if(x2.id>=i-k) break; q2.pop(); } // printf("q1.top id=%lld val=%lld 2=%lld %lld ",x1.id,x1.val,x2.id,x2.val); //f[i]=min(x1.val,x2.val+sum[i]); if(!q1.empty())f[i]=min(0x7ffffffffff,q1.top().val); if(!q2.empty())f[i]=min(0x7ffffffffff,q2.top().val+sum[i]); // printf("f[%lld]=%lld ",i,f[i]); x1.id=i,x1.val=f[i]+b[i]; q1.push(x1); } } int main(){ n=read(),k=read(); for(ll i=1;i<=n;i++){ sum[i]=read(); sum[i]+=sum[i-1]; } for(ll i=0;i<n;i++){ b[i]=read(); } work(); printf("%lld ",f[n]); }