A. Cut it Out!

枚举第一刀,那么之后每切一刀都会将原问题划分成两个子问题。

考虑DP,设$f[l][r]$表示$l$点顺时针一直到$r$点还未切割的最小代价,预处理出每条边的代价转移即可。

时间复杂度$O(n^3)$。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=422;
const double eps=1e-8;
const double inf=1e100;
inline int sgn(double x){
if(x>eps)return 1;
if(x<-eps)return -1;
return 0;
}
inline void up(double&a,double b){if(a>b)a=b;}
int n,m,i,j,k;
double ans=inf,f[N][N];
bool v[N][N];
struct P{
double x,y;
P(){}
P(double _x,double _y){x=_x,y=_y;}
P operator-(const P&b)const{return P(x-b.x,y-b.y);}
P operator+(const P&b)const{return P(x+b.x,y+b.y);}
P operator*(const double&b)const{return P(x*b,y*b);}
double len(){return hypot(x,y);}
double len2(){return x*x+y*y;}
void read(){scanf("%lf%lf",&x,&y);}
}a[N],b[N];
double wl[N],wr[N];
inline double cross(const P&a,const P&b){return a.x*b.y-a.y*b.x;}
inline double line_intersection(const P&a,const P&b,const P&p,const P&q){
double U=cross(p-a,q-p),D=cross(b-a,q-p);
return U/D;
//return a+(b-a)*(U/D);
}
inline void pre(double&A,double&B,int k){
A=-inf,B=inf;
for(int i=0;i<n;i++){
double now=line_intersection(b[k],b[k+1],a[i],a[i+1]);
if(now<0.5&&now>A)A=now;
if(now>0.5&&now<B)B=now;
}
}
inline double cal(int k,int L,int R){
k%=m;
k+=m;
k%=m;
if(L>-100){
L%=m;
L+=m;
L%=m;
}
if(R>-100){
R%=m;
R+=m;
R%=m;
}
double A=wl[k],B=wr[k];
if(L>=0){
double now=line_intersection(b[k],b[k+1],b[L],b[L+1]);
//printf("L=%d %.10f\n",L,now);
if(now<0.5&&now>A)A=now;
if(now>0.5&&now<B)B=now;
}
if(R>=0){
double now=line_intersection(b[k],b[k+1],b[R],b[R+1]);
//printf("R=%d %.10f\n",R,now);
if(now<0.5&&now>A)A=now;
if(now>0.5&&now<B)B=now;
}
//printf("! %.10f\n",(B-A)*((b[k]-b[k+1]).len()));
return (B-A-1)*((b[k]-b[k+1]).len());
}
double dfs(int l,int r){//point a[l] -> a[r] are not cut
if(l>=r)return 0;
if(v[l][r])return f[l][r];
double ret=inf;
for(int i=l;i<r;i++){
//printf("i=%d cal=%.10f\n",i,cal(i,l-1,r));
up(ret,dfs(l,i)+dfs(i+1,r)+cal(i,l-1,r));
}
v[l][r]=1;
//printf("f[%d][%d]=%.10f\n",l,r,f[l][r]);
return f[l][r]=ret;
}
int main(){
scanf("%d",&n);
for(i=0;i<n;i++)a[i].read();
a[n]=a[0];
scanf("%d",&m);
for(i=0;i<m;i++)b[i].read();
b[m]=b[0];
//cal(6,5,8);
//dfs(6,8);
for(i=0;i<m;i++)pre(wl[i],wr[i],i);
for(i=0;i<m;i++)up(ans,dfs(i+1,i+m)+cal(i,-100,-100));
for(i=0;i<m;i++)ans+=(b[i]-b[i+1]).len();
printf("%.15f",ans);
}
/*
4
-100 -100
-100 100
100 100
100 -100
8
-1 -2
-2 -1
-2 1
-1 2
1 2
2 1
2 -1
1 -2
*/

  

B. Double Clique

一个方案合法当且仅当团点数$\times ($团点数$-1)+$独立集度数和$=$团度数和,且存在可行方案满足团是度数最大的若干个点。

找到可行方案后,要么是团里出去一个点,要么是独立集里出去一个点,要么两边各出去一个点。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010;
int n,m,i,j,x,y,d[N],s[N];ll ans;
int main(){
scanf("%d%d",&n,&m);
while(m--)scanf("%d%d",&x,&y),d[x]++,d[y]++;
sort(d+1,d+n+1);
reverse(d+1,d+n+1);
for(i=1;i<=n;i++)s[i]=s[i-1]+d[i];
for(i=0;i<=n;i++)if(1LL*i*(i-1)+s[n]-s[i]==s[i]){ans=1;break;}
if(!ans)return puts("0"),0;
for(j=1;j<=i;j++)if(1LL*(i-1)*(i-2)+s[n]-s[i]+d[j]==s[i]-d[j])ans++;
for(j=i+1;j<=n;j++)if(1LL*(i+1)*i+s[n]-s[i]-d[j]==s[i]+d[j])ans++;
for(x=0,j=1;j<=i;j++)if(d[j]==d[i])x++;
for(y=0,j=i+1;j<=n;j++)if(d[j]==d[i])y++;
ans+=1LL*x*y;
printf("%lld",ans%1000000007);
}

  

C. Flashing Fluorescents

注意到答案不超过$n$,枚举答案$ans$,则任何一个可行方案可以由若干个长度互不相等且不超过$ans$的区间异或得到。

设$f[ans][S]$表示长度不超过$ans$能否异或出$S$,枚举当前长度的区间位置转移即可。

时间复杂度$O(2^nn^2)$。

#include<cstdio>
#include<cstring>
int n,i,j,now,S;
bool f[50][1<<16];
char a[50];
int main(){
scanf("%s",a);
n=strlen(a);
for(i=0;i<n;i++)if(a[i]=='0')S^=1<<i;
f[0][S]=1;
while(!f[now][0]){
for(S=0;S<1<<n;S++)f[now+1][S]=f[now][S];
for(i=0;i<n;i++){
int mask=0;
for(j=0;j<now+1&&i+j<n;j++)mask|=1<<(i+j);
for(S=0;S<1<<n;S++)if(f[now][S])f[now+1][S^mask]=1;
}
now++;
}
printf("%d",now);
}

  

D. Missing Gnomes

按题意模拟。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
int a[N], ans[N];
bool use[N];
int main()
{
while(~scanf("%d%d", &n, &m))
{
for(int i = 1; i <= n; ++i)use[i] = 0; for(int i = 1; i <= m; ++i)
{
scanf("%d", &a[i]);
use[a[i]] = 1;
} int x = 1;
int g = 0;
for(int i = 1; i <= m; ++i)
{
while(x < a[i])
{
if(!use[x])ans[++g] = x;
++x;
}
ans[++g] = a[i];
}
while(x <= n)
{
if(!use[x])ans[++g] = x;
++x;
}
for(int i = 1; i <= n; ++i)printf("%d\n", ans[i]);
}
return 0;
} /*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

E. Prefix Free Code

建立Trie将字符串映射为数字,从前往后枚举LCP,那么这一位能填的数要小于某个值,且前面没出现过,可以用树状数组加速统计,后面能填的数可以用组合数计算。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<cstring>
const int N=1000010,P=1000000007;
int n,m,len,i,j,x,son[N][26],v[N],tot,dfn,a[N],cnt;
int bit[N],fac[N],inv[N],ans;
char s[N];
void dfs(int x){
if(v[x])v[x]=++dfn;
for(int i=0;i<26;i++)if(son[x][i])dfs(son[x][i]);
}
inline void ins(int x,int p){for(;x<=n;x+=x&-x)bit[x]+=p;}
inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
inline int A(int n,int m){return n<m?0:1LL*fac[n]*inv[n-m]%P;}
int main(){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%s",s);
len=strlen(s);
for(x=j=0;j<len;j++){
if(!son[x][s[j]-'a'])son[x][s[j]-'a']=++tot;
x=son[x][s[j]-'a'];
}
v[x]=1;
}
dfs(0);
for(fac[0]=i=1;i<=n;i++)fac[i]=1LL*fac[i-1]*i%P;
for(inv[0]=inv[1]=1,i=2;i<=n;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
for(i=2;i<=n;i++)inv[i]=1LL*inv[i-1]*inv[i]%P;
scanf("%s",s);
for(x=i=0;s[i];i++){
x=son[x][s[i]-'a'];
if(v[x])a[++cnt]=v[x],x=0;
}
ans=1;
for(i=1;i<=n;i++)ins(i,1);
for(i=1;i<=cnt;i++){
ins(a[i],-1);
ans=(1LL*A(n-i,m-i)*ask(a[i])+ans)%P;
}
printf("%d",ans);
}

  

F. Probe Droids

即求斜率第$k$小的坐标,首先特判掉斜率$=0$或者斜率不存在的情况。

在Stern-Brocot Tree上枚举互质数对作为斜率,然后用类欧几里得算法在$O(\log n)$的时间内统计出直线下方的点数,以此来判断往左走还是往右走。

考虑二分往左往右走的拐点位置,则每次询问的复杂度降低至$O(\log^3n)$。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long ll;
ll ansx,ansy;
ll cal(ll a,ll b,ll c,ll n){
if(!a||n<0)return 0;
ll x,y;
if(a>=c||b>=c){
x=cal(a%c,b%c,c,n);
y=a/c*(n*(n+1)/2)+b/c*(n+1)+x;
return y;
}
ll m=(a*n+b)/c;
x=cal(c,c-b-1,a,m-1);
y=n*m-x;
return y;
}
ll calbelow(ll up,ll down,ll n,ll m){
ll lim=min(n*down/up,m);
return cal(up,0,down,lim)+(m-lim)*n;
}
ll calexact(ll up,ll down,ll n,ll m){
return min(n/up,m/down);
}
int check(ll up,ll down,ll n,ll m,ll k){
if(up>n||down>m)return 2;
ll below=calbelow(up,down,n,m);
ll exact=calexact(up,down,n,m);
//cout<<up<<" "<<down<<" "<<below<<" "<<exact<<endl;
//[below-exact+1,below]
if(k>below)return -1;//too small
if(k<below-exact+1)return 1;//too big
return 0;
}
void solve(ll n,ll m,ll k){
ll lu=0,ld=1,ru=1,rd=0,mu,md;
ll A,B;
while(1){
mu=lu+ru;
md=ld+rd;
int ret=check(mu,md,n,m,k);
if(ret==0){
A=mu,B=md;
break;
}
ll l=1,r=n,fin=0;
if(ret<0){
while(l<=r){
ll mid=(l+r)>>1;
if(check(lu+ru*mid,ld+rd*mid,n,m,k)<0)l=(fin=mid)+1;else r=mid-1;
}
lu+=ru*fin,ld+=rd*fin;
}else{
while(l<=r){
ll mid=(l+r)>>1;
if(check(ru+lu*mid,rd+ld*mid,n,m,k)==1)l=(fin=mid)+1;else r=mid-1;
}
ru+=lu*fin,rd+=ld*fin;
}
}
ll below=calbelow(A,B,n,m);
ll exact=calexact(A,B,n,m);
below=below-exact;
k-=below;
ansx=B*k;
ansy=A*k;
//cout<<A<<"/"<<B<<endl;
}
int main(){
ll n,m,q,k;
cin>>n>>m>>q;
while(q--){
cin>>k;
if(k<m){
cout<<"1 "<<k+1<<endl;
continue;
}
if(k>n*m-n){
cout<<k-(n*m-n)+1<<" 1"<<endl;
continue;
}
solve(n-1,m-1,k-(m-1));
cout<<ansy+1<<" "<<ansx+1<<endl;
}
}

  

G. Rainbow Graph

若只有一个限制,满足拟阵。

对于两个限制,则是两个拟阵的交。

首先特判全部都无解的情况,并将全集$E$作为$k=m$的解。

设$M_1$为限制$1$的拟阵,一个方案合法当且仅当在限制$1$下连通,同理定义$M_2$为限制$2$的拟阵。

建立有向图,原图每条边作为一个点,并添加源汇$S$和$T$。

对于上一个$k$的一组最优解$E$中的某条边$x$,如果去掉它后仍然满足$M_1$,则由$S$向$x$连边;若去掉它后仍然满足$M_2$,则由$x$向$T$连边。

对于$E$中某条边$x$和不在$E$中的某条边$y$,若将$x$换成$y$后满足$M_2$,则由$x$向$y$连边;若满足$M_1$,则由$y$向$x$连边。

用SPFA求出$S$到$T$的最短路,就能得到边数恰好减少$1$的最优解。

时间复杂度$O(n^4)$。

#include<cstdio>
const int N=105,M=100000,inf=~0U>>1;
int n,m,i,S,T,x,y,g[N],v[N<<1],nxt[N<<1],ed,vis[N];
int cost[N],col[N],use[N],ans,fin[N];char ch[9];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,char ban){
if(vis[x])return;
vis[x]=1;
for(int i=g[x];i;i=nxt[i])if(use[i>>1]&&col[i>>1]!=ban)dfs(v[i],ban);
}
inline bool check(char ban){
int i;
for(i=1;i<=n;i++)vis[i]=0;
dfs(1,ban);
for(i=1;i<=n;i++)if(!vis[i])return 0;
return 1;
}
namespace Matroid{
int g[N],v[M],nxt[M],ed,q[M],h,t,d[N],pre[N],w[N];bool in[N];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline void ext(int x,int y,int z){
if(d[x]<=y)return;
d[x]=y;
pre[x]=z;
if(in[x])return;
q[++t]=x;
in[x]=1;
}
inline bool find(){
int i,j;
S=m+1,T=m+2;
for(ed=0,i=1;i<=T;i++)g[i]=0;
for(i=1;i<=m;i++)if(use[i]){
w[i]=-cost[i];
use[i]^=1;
if(check('R'))add(S,i);
if(check('B'))add(i,T);
use[i]^=1;
}else w[i]=cost[i];
for(i=1;i<=m;i++)if(use[i])for(j=1;j<=m;j++)if(!use[j]){
use[i]^=1,use[j]^=1;
if(check('B'))add(i,j);
if(check('R'))add(j,i);
use[i]^=1,use[j]^=1;
}
for(i=1;i<=T;i++)d[i]=inf,in[i]=0;
q[h=t=1]=S;
d[S]=0,in[S]=1;
while(h<=t){
x=q[h++];
//printf("! %d %d %d\n",x,d[x],pre[x]);
for(i=g[x];i;i=nxt[i])ext(v[i],d[x]+w[v[i]],x);
in[x]=0;
}
if(d[T]==inf)return 0;
ans+=d[T];
while(pre[T]!=S)use[T=pre[T]]^=1;
return 1;
}
}
int main(){
scanf("%d%d",&n,&m);
for(ed=i=1;i<=m;i++){
scanf("%d%d%d%s",&x,&y,&cost[i],ch);
col[i]=ch[0];
add(x,y),add(y,x);
use[i]=1;
ans+=cost[i];
}
if(!check('R')||!check('B')){
for(i=1;i<=m;i++)puts("-1");
return 0;
}
fin[m]=ans;
for(i=m-1;i;i--)if(Matroid::find())fin[i]=ans;
else{
for(x=1;x<=i;x++)fin[x]=-1;
break;
}
for(i=1;i<=m;i++)printf("%d\n",fin[i]);
}

  

H. Recovery

将所有位置都设成$1$,然后贪心配对行列使得满足条件。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
char a[55], b[55];
int aa[55], bb[55];
int ga, gb;
char s[55][55];
int va[55], vb[55];
bool rand(char a[])
{
int n = random() % 4 + 1;
for(int i = 0; i < n; ++i)a[i] = rand() % 2 + '0';
a[n] = 0;
return 1;
} char tt[55][55];
char t[55][55];
bool FLAG;
void BF()
{
FLAG = 0;
int w = n * m;
int top = 1 << w;;
int ansone = -1;
for(int i = 0; i < top; ++i)
{
for(int j = 0; j < w; ++j)
{
tt[n - 1 - j / m][m - 1 - j % m] = '0' + (i >> j & 1);
}
MS(va, 0);
MS(vb, 0);
int one = 0;
for(int j = 0; j < n; ++j)
{
for(int k = 0; k < m; ++k)
{
va[j] += tt[j][k] % 2;
vb[k] += tt[j][k] % 2;
one += tt[j][k] % 2;
}
}
bool flag = 1;
for(int j = 0; j < n; ++j)if(va[j] % 2 != a[j] % 2)
{
flag = 0;
break;
}
for(int j = 0; j < m; ++j)if(vb[j] % 2 != b[j] % 2)
{
flag = 0;
break;
}
if(flag)
{
FLAG = 1;
if(one > ansone)
{
ansone = one;
memcpy(t, tt, sizeof(tt));
}
}
}
} int main()
{
while(~scanf("%s%s", a, b))
//while(rand(a), rand(b))
{
n = strlen(a);
m = strlen(b);
//puts("input"); puts(a); puts(b); MS(s, 0);
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
s[i][j] = '1';
}
}
ga = gb = 0;
for(int i = 0; i < n; ++i)
{
if(m % 2 != a[i] % 2)
{
aa[++ga] = i;
}
}
for(int i = 0; i < m; ++i)
{
if(n % 2 != b[i] % 2)
{
bb[++gb] = i;
}
} //BF();
if(ga + gb & 1)
{
puts("-1");
/*
if(FLAG)
{
puts("NO flag");
while(1);
}
*/
}
else
{
/*
if(!FLAG)
{
puts("NO !flag");
while(1);
}
*/ //printf("ga|gb = %d %d\n", ga, gb); while(ga < gb)aa[++ga] = 0;
while(gb < ga)bb[++gb] = 0;
int g = ga; /*
int g = min(ga, gb);
for(int i = g + 1; i <= ga; ++i)
{
bb[++gb] = 0;
}
for(int i = g + 1; i <= gb; ++i)
{
aa[++ga] = 0;
}
*/ sort(aa + 1, aa + ga + 1);
sort(bb + 1, bb + gb + 1);
/*printf("ga|gb = %d %d\n", ga, gb);
for(int i = 1; i <= ga; ++i)printf("%d ", aa[i]); puts("");
for(int i = 1; i <= gb; ++i)printf("%d ", bb[i]); puts("");
*/
g = max(ga, gb); for(int i = 1; i <= g; ++i)
{
s[aa[i]][bb[i]] = '0';
} for(int i = 0; i < n; ++i)puts(s[i]); /*
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
if(s[i][j] != t[i][j])
{
puts("s[i][j] != t[i][j]"); for(int i = 0; i < n; ++i)puts(s[i]);
for(int i = 0; i < n; ++i)puts(t[i]);
while(1);
}
}
}
*/ /*
MS(va, 0);
MS(vb, 0);
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
va[i] += s[i][j] % 2;
vb[j] += s[i][j] % 2;
}
}
for(int i = 0; i < n; ++i)
{
if(va[i] % 2 != a[i] % 2)
{
puts("NO A");
while(1);
}
}
for(int i = 0; i < m; ++i)
{
if(vb[i] % 2 != b[i] % 2)
{
puts("NO B");
while(1);
}
}
*/
}
}
return 0;
} /*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

I. Red Black Tree

设$f[i][j]$表示考虑$i$的子树,里面选了$j$个红点的方案数,转移时只枚举有效的$j$即可得到$O(nm)$的时间复杂度。

#include<cstdio>
const int N=200010,M=1005,P=1000000007;
int n,m,i,x,g[N],nxt[N],size[N],vip[N];
int f[N][M],h[M];
void dfs(int x){
size[x]=vip[x];
//case 1 not choose x
f[x][0]=1;
for(int y=g[x];y;y=nxt[y]){
dfs(y);
for(int j=0;j<=size[x]+size[y]&&j<=m;j++)h[j]=0;
for(int j=0;j<=size[y]&&j<=m;j++)for(int k=0;k<=size[x]&&j+k<=m;k++)
h[j+k]=(1LL*f[y][j]*f[x][k]+h[j+k])%P;
size[x]+=size[y];
for(int j=0;j<=size[x]+size[y]&&j<=m;j++)f[x][j]=h[j];
}
//case 2 choose x
f[x][vip[x]]=(f[x][vip[x]]+1)%P;
}
int main(){
scanf("%d%d",&n,&m);
for(i=2;i<=n;i++){
scanf("%d",&x);
nxt[i]=g[x];g[x]=i;
}
for(i=1;i<=m;i++)scanf("%d",&x),vip[x]=1;
dfs(1);
for(i=0;i<=m;i++)printf("%d\n",f[1][i]);
}

  

J. Winter Festival

因为所有简单环边权和都要是奇数,因此当两个简单环有公共边时不可能满足条件,所以当且仅当图是仙人掌的时候才有解。

设$f[i][j][x][y]$表示考虑DFS生成树中$i$的子树,$i$与$i$父亲的边的边权为$j$,$i$与$i$父亲的边所在环的边权和模$2$为$x$,$i$与$i$父亲的边所在环的非树边边权为$y$的最小代价。

转移时需要通过另一个辅助数组$h[S][j][x][y]$来进行不同子树的合并,其中$j,x,y$含义与$f$相同,而$S$表示$x$点周围存在的边权集合。

时间复杂度$O(n+m)$。

#include<cstdio>
#include<cstdlib>
#define rep(i,n) for(int i=0;i<n;i++)
using namespace std;
const int N=100010,inf=100000000;
int n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed;
int mark[N],fa[N],vis[N],dfn;
int f[N][3][2][3];
int dp[1<<3][2][3],h[1<<3][2][3];
int istop[N],isbot[N];
int ban[3][1<<3];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline void up(int&a,int b){a>b?(a=b):0;}
inline void clr(){
rep(S,8)rep(j,2)rep(k,3)h[S][j][k]=inf;
}
inline void go(){
rep(S,8)rep(j,2)rep(k,3)dp[S][j][k]=h[S][j][k];
}
void dfs(int x,int y){
fa[x]=y;
vis[x]=++dfn;
for(int i=g[x];i;i=nxt[i]){
int u=v[i];
if(u==y)continue;
if(!vis[u]){
dfs(u,x);
}else if(vis[u]<vis[x]){
int j=x;
isbot[x]=1;
while(j!=u){
mark[j]++;
if(mark[j]>1){
puts("-1");
exit(0);
}
if(fa[j]==u)istop[j]=1;
j=fa[j];
}
}
}
rep(S,8)rep(j,2)rep(k,3)dp[S][j][k]=inf;
if(!y)dp[0][0][0]=0;
else{
//add the cycle edge
if(isbot[x])rep(c,3)up(dp[1<<c][c&1][c],c);
else dp[0][0][0]=0;
}
for(int i=g[x];i;i=nxt[i]){
int u=v[i];
if(u==y)continue;
if(fa[u]==x){
clr();
if(istop[u]){
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(A,3)if(!ban[A][S])rep(B,2)rep(C,3)if(f[u][A][B][C]<inf){
if(B!=1)continue;
if(ban[C][S])continue;
if((A+C)%3==1)continue;
up(h[S|(1<<A)|(1<<C)][j][k],dp[S][j][k]+f[u][A][B][C]);
}
}else if(mark[u]){
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(A,3)if(!ban[A][S])rep(B,2)rep(C,3)if(f[u][A][B][C]<inf){
up(h[S|(1<<A)][(j+B)&1][C],dp[S][j][k]+f[u][A][B][C]);
}
}else{
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(A,3)if(!ban[A][S])rep(B,1)rep(C,1)if(f[u][A][B][C]<inf){
up(h[S|(1<<A)][j][k],dp[S][j][k]+f[u][A][B][C]);
}
}
go();
}
}
rep(S,3)rep(j,2)rep(k,3)f[x][S][j][k]=inf;
if(y){
//add the father edge
if(mark[x]){
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(c,3)if(!ban[c][S])up(f[x][c][(j+c)&1][k],dp[S][j][k]+c);
}else{
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
rep(c,3)if(!ban[c][S])up(f[x][c][j][k],dp[S][j][k]+c);
}
}else{
rep(S,8)rep(j,2)rep(k,3)if(dp[S][j][k]<inf)
up(f[x][0][0][0],dp[S][j][k]);
}
}
int main(){
rep(i,3)rep(j,8)rep(k,3)if((j>>k&1)&&(i+k)%3==1)ban[i][j]=1;
scanf("%d%d",&n,&m);
for(ed=i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
int ans=0;
for(i=1;i<=n;i++)if(!vis[i]){
dfs(i,0);
if(f[i][0][0][0]>=inf){
puts("-1");
exit(0);
}
ans+=f[i][0][0][0];
}
printf("%d",ans);
}

  

K. Zoning Houses

若不删除任何点,则答案为区间$x$坐标的极差与$y$坐标极差的较大值。

若删除一个点,则最优方案下一定是删除$x$或者$y$坐标最小或者最大的$4$个点之一,线段树维护即可。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int,int>P;
const int N=100010,M=262150,inf=1000000010;
int n,m,i,x,y,ans;
P xmi[M],xma[M],ymi[M],yma[M];
void build(int x,int a,int b){
if(a==b){
scanf("%d%d",&xmi[x].first,&ymi[x].first);
xmi[x].second=ymi[x].second=a;
xma[x]=xmi[x];
yma[x]=ymi[x];
return;
}
int mid=(a+b)>>1;
build(x<<1,a,mid),build(x<<1|1,mid+1,b);
xmi[x]=min(xmi[x<<1],xmi[x<<1|1]);
xma[x]=max(xma[x<<1],xma[x<<1|1]);
ymi[x]=min(ymi[x<<1],ymi[x<<1|1]);
yma[x]=max(yma[x<<1],yma[x<<1|1]);
}
P askxmi(int x,int a,int b,int c,int d){
if(c>d)return P(inf,0);
if(c<=a&&b<=d)return xmi[x];
int mid=(a+b)>>1;
P t(inf,0);
if(c<=mid)t=askxmi(x<<1,a,mid,c,d);
if(d>mid)t=min(t,askxmi(x<<1|1,mid+1,b,c,d));
return t;
}
P askymi(int x,int a,int b,int c,int d){
if(c>d)return P(inf,0);
if(c<=a&&b<=d)return ymi[x];
int mid=(a+b)>>1;
P t(inf,0);
if(c<=mid)t=askymi(x<<1,a,mid,c,d);
if(d>mid)t=min(t,askymi(x<<1|1,mid+1,b,c,d));
return t;
}
P askxma(int x,int a,int b,int c,int d){
if(c>d)return P(-inf,0);
if(c<=a&&b<=d)return xma[x];
int mid=(a+b)>>1;
P t(-inf,0);
if(c<=mid)t=askxma(x<<1,a,mid,c,d);
if(d>mid)t=max(t,askxma(x<<1|1,mid+1,b,c,d));
return t;
}
P askyma(int x,int a,int b,int c,int d){
if(c>d)return P(-inf,0);
if(c<=a&&b<=d)return yma[x];
int mid=(a+b)>>1;
P t(-inf,0);
if(c<=mid)t=askyma(x<<1,a,mid,c,d);
if(d>mid)t=max(t,askyma(x<<1|1,mid+1,b,c,d));
return t;
}
inline int cal(int x,int y,int z){
return max(
max(askxma(1,1,n,x,z-1).first,askxma(1,1,n,z+1,y).first)-min(askxmi(1,1,n,x,z-1).first,askxmi(1,1,n,z+1,y).first)
,
max(askyma(1,1,n,x,z-1).first,askyma(1,1,n,z+1,y).first)-min(askymi(1,1,n,x,z-1).first,askymi(1,1,n,z+1,y).first)
);
}
int main(){
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--){
scanf("%d%d",&x,&y);
ans=cal(x,y,askxmi(1,1,n,x,y).second);
ans=min(ans,cal(x,y,askxma(1,1,n,x,y).second));
ans=min(ans,cal(x,y,askymi(1,1,n,x,y).second));
ans=min(ans,cal(x,y,askyma(1,1,n,x,y).second));
printf("%d\n",ans);
}
}

  

North American Invitational Programming Contest 2018的更多相关文章

  1. The North American Invitational Programming Contest 2018 D. Missing Gnomes

    A family of nn gnomes likes to line up for a group picture. Each gnome can be uniquely identified by ...

  2. The North American Invitational Programming Contest 2018 H. Recovery

    Consider an n \times mn×m matrix of ones and zeros. For example, this 4 \times 44×4: \displaystyle \ ...

  3. The North American Invitational Programming Contest 2018 E. Prefix Free Code

    Consider nn initial strings of lower case letters, where no initial string is a prefix of any other ...

  4. The North American Invitational Programming Contest 2017 题目

    NAIPC 2017 Yin and Yang Stones 75.39% 1000ms 262144K   A mysterious circular arrangement of black st ...

  5. North American Invitational Programming Contest (NAIPC) 2017

    (待补) A. Pieces of Parentheses 将括号处理完成后排序,方式参加下面的博客.然后做一遍背包即可. 2018 Multi-University Training Contest ...

  6. North American Invitational Programming Contest (NAIPC) 2016

    (待补) A. Fancy Antiques 爆搜. B. Alternative Bracket Notation C. Greetings! D. Programming Team 0/1分数规划 ...

  7. AtCoder SoundHound Inc. Programming Contest 2018 E + Graph (soundhound2018_summer_qual_e)

    原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-SoundHound-Inc-Programming-Contest-2018-E.html 题目 ...

  8. ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2018) Syria, Lattakia, Tishreen University, April, 30, 2018

    ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2018) Syr ...

  9. German Collegiate Programming Contest 2018​ B. Battle Royale

    Battle Royale games are the current trend in video games and Gamers Concealed Punching Circles (GCPC ...

随机推荐

  1. GCC __builtin_expect的作用

    https://blog.csdn.net/shuimuniao/article/details/8017971 #define LIKELY(x) __builtin_expect(!!(x), 1 ...

  2. BZOJ 3674 可持久化并查集

    https://www.lydsy.com/JudgeOnline/problem.php?id=3674 用可持久化数组维护并查集的fa数组, 查询时间复杂度为nlognlogn,一个log是并查集 ...

  3. 剑指Offer_编程题_19

    题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2, ...

  4. [物理学与PDEs]第3章第2节 磁流体力学方程组 2.2 考虑到电磁场的存在对流体力学方程组的修正

    1.  连续性方程 $$\bex \cfrac{\p \rho}{\p t}+\Div(\rho{\bf u})=0.  \eex$$ 2.  动量守恒方程 $$\bex \cfrac{\p }{\p ...

  5. Java8从对象列表中取出某个属性的列表

    List<属性值类型> 属性List = 对象List.stream().map(对象::get方法()).collect(Collectors.toList()); 例如: List&l ...

  6. 字符串常量池和String.intern()方法在jdk1.6、1.7、1.8中的变化

    字符串常量池也是运行时常量池 jdk1.6中,它是在方法区中,属于“永久代” jdk1.7中,它被移除方法区,放在java堆中 jdk1.8中,取消了“永久代”,将常量池放在元空间,与堆独立了 pub ...

  7. MapReduce Partition解析

    Map的结果,会通过partition分发到Reducer上,reducer操作过后会进行输出.输出的文件格式后缀000001就代表1分区. Mapper处理过后的键值对,是需要送到Reducer那边 ...

  8. [Harbor]Harbor简要介绍

    前一段时间写过一篇文章:[Kubernetes]CentOS7下搭建Harbor仓库,只是知道了如何搭建,但是对于背后的整体架构还不是太清楚,这篇文章就来讲讲. 默认情况下,Harbor运行起来后有如 ...

  9. Linux Django项目部署

    步骤 .数据库的处理 1.1 上传bbs.sql 1.2 在mysql中创建bbs库,并导入数据库SQL脚本 mysql> create database bbs charset utf8mb4 ...

  10. Thymleaf中th:each标签遍历list如何获取index

    简单介绍:传递给后台一个String类型的list,需要获取到list的每一个元素,然后进行筛选,得到正确的文本值,看代码就明白了 代码: //后台java代码//failList是一个String类 ...