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. BZOJ 3674 可持久化并查集

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

  2. 前向分步算法 && AdaBoost算法 && 提升树(GBDT)算法 && XGBoost算法

    1. 提升方法 提升(boosting)方法是一种常用的统计学方法,在分类问题中,它通过逐轮不断改变训练样本的权重,学习多个分类器,并将这些分类器进行线性组合,提高分类的性能 0x1: 提升方法的基本 ...

  3. .net异步委托

    委托Delegate是一个类,定义了方法的类型, 使得可以将方法当做另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大佬使用If-Else(Switch)语句,同时使得程序 ...

  4. docker下安装mysql

    docker run -d -p 3306:3306 -v /root/docker/mysql/conf/mysql.cnf:/etc/mysql/conf.d/mysql.cnf -v /root ...

  5. nginx使用ssl模块配置支持HTTPS访问,腾讯云申请免费证书

    开始我尝试用 let's encrypt 让http 变 https 官方:https://github.com/certbot/certbot 参考:https://www.cnblogs.com/ ...

  6. [物理学与PDEs]第1章第3节 真空中的 Maxwell 方程组, Lorentz 力 3.2 Lorentz 力

    1. Lorentz 假定, 不论带电体的运动状态如何, 其所受的力密度 (单位体积所受的力) 为 $$\bex {\bf F}=\rho {\bf E}+{\bf j}\times{\bf B} = ...

  7. MYSQL(三)

    转载自https://www.cnblogs.com/wupeiqi/articles/5716963.html 1.索引 索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数 ...

  8. 智联python 技能摘取

  9. springboot上传文件 & 不配置虚拟路径访问服务器图片 & springboot配置日期的格式化方式 & Springboot配置日期转换器

    1.    Springboot上传文件 springboot的文件上传不用配置拦截器,其上传方法与SpringMVC一样 @RequestMapping("/uploadPicture&q ...

  10. Android 控件背景选择图片还是drawable XML资源

    决定一个控件应该是否用Drawable XML渲染,应考虑以下几个因素: * App是否要支持多分辨率: * App是否有瘦身的需要: * 图案是否足够简单: * 图案需要自由缩放: * 设计开发工作 ...