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#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]) return printf("%lld\n",sum),0; if(min(a[i][0],a[i][1])>sta[n]) return printf("%lld\n",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\n",ans); }
D - Chords
感觉看到这个思路之后有种被戏耍的感觉。
我一直在想一种复杂的dp,想的我都怀疑人生了。
统计$x\epsilon S$的$Min,Max$,记做$f[Min][Max]$,直接减去分成很多块的情况就行了。
记着尝试整体贡献拆成单个的联通块的贡献啊!!!


#include#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] 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< endl; }
E - High Elements
不知道出题人为什么这么厉害。
首先按位确定是显然的,关键在怎么$check$。
$maxa $maxb $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$就行了。 没有题解,只好看了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
F(F2) - Reachable Cells
#include