C - Min Cost Cycle
思路好6啊,考试想了半天都没有想出来。
一直在想一个错误的贪心算法。
首先,我们把加一条权值为$min(Ax,By)$的边变成两条权值分别为$Ax,By$的边。
然后点就可以分成四类$(0,0),(0,1),(1,0),(1,1)$代表入边出边是否选自己的。
如果存在$(0,0)$,那么$(1,1)$个数一定和它相等。
并且我们发现$(0,0)+(1,0)+(1,0)......=(0,0)$,同理$(1,1)+(0,1)$也是。
而且$(0,0)$和相等数量的$(1,1)$可以合并成一个环,这样就一定存在合法方案。
也就是说我们只需要保证存在一个$(1,1)$即可,直接枚举计算答案即可。
对于不存在的直接算就行了。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 500005 int n; int a[M][2],sta[M]; int main () { //freopen("a.in","r",stdin); n=read(); for1(1,n,i) { a[i][0]=read(); a[i][1]=read(); sta[i*2]=a[i][1]; sta[i*2-1]=a[i][0]; } sort(sta+1,sta+2*n+1); ll ans=0,sum=0; for1(1,n,i) ans+=a[i][0],sum+=a[i][1]; ans=min(ans,sum),sum=0; for1(1,n,i) sum+=sta[i]; for1(1,n,i) { if(max(a[i][0],a[i][1])<sta[n]) return printf("%lld ",sum),0; if(min(a[i][0],a[i][1])>sta[n]) return printf("%lld ",sum),0; ll now=sum; now+=max(a[i][0],a[i][1])-sta[n]; now+=max(0,min(a[i][0],a[i][1])-sta[n-1]); ans=min(ans,now); } printf("%lld ",ans); }
D - Chords
感觉看到这个思路之后有种被戏耍的感觉。
我一直在想一种复杂的dp,想的我都怀疑人生了。
统计$xepsilon S$的$Min,Max$,记做$f[Min][Max]$,直接减去分成很多块的情况就行了。
记着尝试整体贡献拆成单个的联通块的贡献啊!!!
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 605 #define mod 1000000007 int n,K; int pos[M],sum[M]; int g[M],f[M][M],ans; inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} inline void dp(int l,int r) { if(sum[r]-sum[l-1]&1) return; for1(l,r,i) if(pos[i]&&pos[i]<l||pos[i]>r) return; int x=g[sum[r]-sum[l-1]]; for1(l,r-1,i) if(f[l][i]) inc(x,mod-1ll*f[l][i]*g[sum[r]-sum[i]]%mod); f[l][r]=x; inc(ans,1ll*x*g[n-2*K-sum[r]+sum[l-1]]%mod); } int main () { //freopen("a.in","r",stdin); n=read()*2,K=read(); for1(1,K,i) { int x=read(),y=read(); pos[x]=y,pos[y]=x; } g[0]=1; for(int i=2;i<=n;i+=2) g[i]=1ll*(i-1)*g[i-2]%mod; for1(1,n,i) sum[i]=sum[i-1]+(!pos[i]); for1(1,n,i) FOR2(n-i+1,1,j) dp(j,j+i-1); cout<<ans<<endl; }
E - High Elements
不知道出题人为什么这么厉害。
首先按位确定是显然的,关键在怎么$check$。
$maxa<a1<a2......<a|a|$
$maxb<b1<b2......<b|b|$
$lena+|a|==lenb+|b|$
对于原序列中的$high$,一定在分配后的序列中也是$high$。
性质:若有解,一定存在一个$|a|,|b|$使得$|a|(|b|)$全部由$high$组成。
如果都有不是$high$直接交换一下序列就都减少了一。
设后面一共有$Q$个$high$,给了$b$序列$k$个,给了$b$序列$m$个非$high$。
$lena+Q-k==lenb+k+m$,即$2*k+m==lena+Q-lenb$。
我们可以把前面的式子看作存在一个上升序列,$high$权值为2,否则为1的权值和。
显然若存在$max$,$max-2$一定存在,然后分奇偶维护$max$就行了。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 1000005 int n; int vis[M]; int a[M],f[M][2],Max[M*4][2]; inline pair<int,int> query(int g,int l,int r,int lx,int rx) { if(lx>rx) return make_pair(0,0); if(lx<=l&&rx>=r) return make_pair(Max[g][0],Max[g][1]); pair<int,int> ans=make_pair(0,0); int mid=l+r>>1; if(lx<=mid) ans=query(g<<1,l,mid,lx,rx); if(rx>mid) { pair<int,int> y=query(g<<1|1,mid+1,r,lx,rx); ans.first=max(ans.first,y.first); ans.second=max(ans.second,y.second); } return ans; } inline void T_add(int g,int l,int r,int x,int id) { if(l==r) return Max[g][0]=f[id][0],Max[g][1]=f[id][1],void(); int mid=l+r>>1; if(x<=mid) T_add(g<<1,l,mid,x,id); else T_add(g<<1|1,mid+1,r,x,id); Max[g][0]=max(Max[g<<1][0],Max[g<<1|1][0]); Max[g][1]=max(Max[g<<1][1],Max[g<<1|1][1]); } inline bool check(int val,int to) { if(to<0) return 0; pair<int,int> y=query(1,1,n,val+1,n); if(!(y.first-to&1)) return y.first>=to; else return y.second>=to; } int main () { //freopen("a.in","r",stdin); n=read(); for1(1,n,i) a[i]=read(); for(int x=0,i=1;i<=n;++i) { x=max(x,a[i]); if(x==a[i]) ++vis[i]; } FOR2(n,1,i) vis[i]+=vis[i+1]; FOR2(n,1,i) { int t=1+(vis[i]!=vis[i+1]); pair<int,int> y=query(1,1,n,a[i]+1,n); f[i][y.first+t&1]=max(f[i][y.first+t&1],y.first+t); f[i][y.second+t&1]=max(f[i][y.second+t&1],y.second+t); T_add(1,1,n,a[i],i); } if(!check(0,vis[1])) return puts("-1"),0; for(int i=1,len[2]={0},pos[2]={0};i<=n;++i) { f[i][0]=f[i][1]=0; T_add(1,1,n,a[i],i); int x=a[i]>a[pos[0]]?i:pos[0],y=len[0]+(x==i); if(check(a[x],len[1]+vis[i+1]-y)||check(a[pos[1]],y+vis[i+1]-len[1])) { pos[0]=x,len[0]=y,putchar('0'); } else { pos[1]=a[i]>a[pos[1]]?i:pos[1]; len[1]+=pos[1]==i; putchar('1'); } } puts(""); }
F(F2) - Reachable Cells
没有题解,只好看了kczno1的代码。
智障地以为维护的是可达的第一个区间,然后就是过不去。
只能去问本人,这才想通了。
复杂度是$O(n^3)$的,不过常数很小,跑9s 1500挺轻松的。
定义$f[i][j]$为$(i,j)$能到达的所有点的权值之和。
显然只有当$(i+1,j)(i,j+1)$都没被占时我们需要减去重复的。
$l[i][j],r[i][j]$维护的是对应$(now,j)$这个点转移完之后,在第i行可以达到的最大(小)点。
这样判断是否有路径的交叉就可以用$(l[i][j],r[i][j])$和$(l[i][j+1],r[i][j+1])$比较了。
显然$l[i][j]<=l[i][j+1],r[i][j]<=r[i][j+1]$。
若$l[i][j+1]<=r[i][j]$,$(l[i][j+1],r[i][j])$一定都是数字。
我们相当于将重复计算的点分成很多个联通块,每个联通块都存在一个点$(x,y)$可以到达剩下的所有点。
证明的话类似$noip$引水入城,判一下路径相交就行了。
显然$(xi,yi)$实在不断的向右下角走的,我们只需判一下这一行的交点是否大于之前的右端点就行了。
数组两维换一下是为了卡常。。。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; #define M 2005 int n; char a[M][M]; int f[M][M],l[M][M],r[M][M]; inline void max(int &x,int y) {if(x<y) x=y;} inline void min(int &x,int y) {if(x>y) x=y;} int main () { //freopen("a.in","r",stdin); scanf("%d",&n); for1(1,n,i) scanf("%s",a[i]+1); ll ans=0; FOR2(n,1,i) FOR2(n,1,j) if(isdigit(a[i][j])) { bool t[2]={isdigit(a[i+1][j]),isdigit(a[i][j+1])}; if(!t[0]&&!t[1]) { l[j][i]=r[j][i]=j; for1(i+1,n,k) l[j][k]=n+1,r[j][k]=0; } else if(!t[0]) { f[i][j]=f[i][j+1]; l[j][i]=j,r[j][i]=r[j+1][i]; for1(i+1,n,k) l[j][k]=l[j+1][k],r[j][k]=r[j+1][k]; } else if(!t[1]) { f[i][j]=f[i+1][j]; l[j][i]=r[j][i]=j; } else { int las=0; int tot=f[i+1][j]+f[i][j+1]; l[j][i]=j,r[j][i]=r[j+1][i]; for1(i+1,n,k) { if(l[j+1][k]<=r[j][k]&&l[j+1][k]>las) tot-=f[k][l[j+1][k]]; max(las,r[j][k]); min(l[j][k],l[j+1][k]); max(r[j][k],r[j+1][k]); } f[i][j]=tot; } ans+=f[i][j]*(a[i][j]-'0'); f[i][j]+=a[i][j]-'0'; } else l[j][i]=n+1,r[j][i]=0; cout<<ans<<endl; }