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. 线性布局LinearLayout

    常用属性 id:控件唯一属性 android:id="@+id/ll_1" --------------------------------------- layout_width ...

  2. Redis配置sentinel模式

    Redis配置sentinel模式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 哨兵(sentinel)主要是完成三个功能:监控,通知,自动故障转移功能.sentinel是安装R ...

  3. C++回顾day03---<多态>

    一:错误理解下的多态 #include <iostream> using namespace std; class Parent { public: Parent() { cout < ...

  4. MySQL安全配置向导mysql_secure_installation详解

    安装完mysql-server 会提示可以运行mysql_secure_installation.运行mysql_secure_installation会执行几个设置:  a)为root用户设置密码  ...

  5. DNS服务器 知识点

    DNS服务器: 1.DNS: Domain Name Service 域 名字 服务 2.域名组成:(树形结构) 根域 .顶级域 国家顶级域 cn jp hk uk 商业顶级域 com 商业机构 go ...

  6. 基于STM32F1的局域网通信模块W5500驱动

    目录 说明 W5500 W5500.c 使用方法 说明 需要调整的内容为W5500.h中关于IP地址.端口号.子网掩码.网关等参数 W5500 #ifndef _W5500_H_ #define _W ...

  7. python中的转义字符

    当我们需要在字符中添加特殊符号时,我们需要用\(即反斜杠来转义字符) 常用的转义字符: 注:如果不想转义添加的特殊字符,需要显示字符串原来的意思的时候,需要用r或R来定义 结果是这样的

  8. mysql 原理 ~ 并行复制

    一 概念1 MTS(Prepared transactions slave parallel applier)   主库在同一时间进入prepare状态的事务可以被从库并行回放2 传统与改进   ma ...

  9. Linux下间隔多少秒 (即以秒为单位) 去执行某条命令或某个shell脚本的操作方法【转】

    在日常运维工作中, 经常会碰到以秒为单位去定时执行某些命令或监控脚本的需求. 说到定时任务就要用到crontab,通常来说,crontab的最小单位是分钟级别,要想实现秒级别的定时任务,就要进行特殊设 ...

  10. Laravel 服务容器,IoC,DI

    DI DI 就是常说的依赖注入,那么究竟什么是依赖注入呢? 打个比方,电脑(非笔记本哈)需要键盘和鼠标我们才能进行操作,这个‘需要’换句话说就是‘依赖’键盘和鼠标. 那么,相应的,一个类需要另一个类才 ...