XVI Open Cup named after E.V. Pankratiev. GP of Ukraine

A. Associated Vertices

首先求出SCC然后缩点,第一次求出每个点能到的点集,第二次收集这些点集即可,用bitset加速,时间复杂度$O(\frac{nm}{64})$。

#include<cstdio>
#include<bitset>
using namespace std;
const int N=10010;
int n,m,x,y,i,j,g[N],G[N],v[N*3],V[N*3],nxt[N*3],NXT[N*3],ed;
int vis[N],q[N],h,t;
int f[N],size[N],d[N],ans;
bitset<N>dp[N];
inline void add(int x,int y){
  v[++ed]=y;nxt[ed]=g[x];g[x]=ed;
  V[++ed]=x;NXT[ed]=G[y];G[y]=ed;
}
inline void ADD(int x,int y){
  if(x==y)return;
  V[++ed]=y;NXT[ed]=G[x];G[x]=ed;
  d[y]++;
}
void dfs1(int x){
  vis[x]=1;
  for(int i=g[x];i;i=nxt[i])if(!vis[v[i]])dfs1(v[i]);
  q[++t]=x;
}
void dfs2(int x,int y){
  vis[x]=0;
  f[x]=y;
  size[y]++;
  for(int i=G[x];i;i=NXT[i])if(vis[V[i]])dfs2(V[i],y);
}
int main(){
  scanf("%d%d",&n,&m);
  while(m--){
    scanf("%d%d",&x,&y);
    add(x,y);
  }
  for(i=1;i<=n;i++)if(!vis[i])dfs1(i);
  for(i=n;i;i--)if(vis[q[i]])dfs2(q[i],q[i]);
  for(i=1;i<=n;i++)G[i]=0;
  for(i=1;i<=n;i++)for(j=g[i];j;j=nxt[j])ADD(f[i],f[v[j]]);
  for(h=i=1,t=0;i<=n;i++)if(f[i]==i&&!d[i])q[++t]=i;
  while(h<=t){
    x=q[h++];
    for(i=G[x];i;i=NXT[i])if(!(--d[V[i]]))q[++t]=V[i];
  }
  for(i=1;i<=n;i++)dp[f[i]][i]=1;
  for(i=t;i;i--){
    x=q[i];
    for(j=G[x];j;j=NXT[j])dp[x]|=dp[V[j]];
  }
  for(i=1;i<=t;i++){
    x=q[i];
    ans+=size[x]*dp[x].count();
    for(j=G[x];j;j=NXT[j])dp[V[j]]|=dp[x];
  }
  printf("%d",ans);
}

  

B. Bishops

容斥。

#include<bits/stdc++.h>
const int N=1000010;
typedef long long LL;
const int Maxn=1000020;
LL a[Maxn*2];
int ok1[Maxn*2];
int ok2[Maxn*2];
int n,m;
int get1(int x){
	if(x>n+1){
		return n-(x-n-1);
	}
	return x-1;
}
int get2(int x){
	if(x<0)return n-(-x);
	return n-x;
}
LL  ask(int l,int r){LL ret=a[r];if(l>=2)ret-=a[l-2];return ret;}
int main(){
	while(scanf("%d%d",&n,&m)!=EOF){
		LL ans=0;
		memset(ok1,0,sizeof ok1);
		memset(ok2,0,sizeof ok2);
		memset(a,0,sizeof a);
		for(int i=0;i<m;i++){
			int x,y;scanf("%d%d",&x,&y);
			if(!ok1[x+y]){
				ans+=get1(x+y);
				ok1[x+y]=1;
				a[x+y]=1;
			}
			if(!ok2[x-y+n]){
				ans+=get2(x-y);
				ok2[x-y+n]=1;
			}
			//printf("%lld\n",ans);
		}
		for(int i=2;i<=n+n;i++)a[i]+=a[i-2];
		for(int i=n;i>=1;i--){
			if(!ok2[i-1+n])continue;
			ans-=ask(i+1,2*n-(i-1));
		}
		for(int i=2;i<=n;i++){
			if(!ok2[1-i+n])continue;
			ans-=ask(i+1,2*n-(i-1));
		}
		printf("%lld\n",1LL*n*n-ans);
	}
}

  

C. Cool Numbers

暴力枚举答案即可。

#include<cstdio>
const int N=1000010;
int i,j,k,cnt,q[N],v[N],is[N],ans;
int rev(int x){
  int t=0;
  while(x)t=t*10+x%10,x/=10;
  return t;
}
int main(){
  for(i=2;i<=1000000;i++)if(!v[i]){
    is[i]=1;
    for(j=i+i;j<=1000000;j+=i)v[j]=1;
  }
  for(i=10;i<=1000000;i++)if(is[i]){
    j=rev(i);
    if(i==j)continue;
    if(is[j]){
      q[++cnt]=i;
      //if(cnt<=30)printf("%d %d\n",i,j);
    }
  }
  scanf("%d",&k);
  if(k>cnt)ans=-1;else ans=q[k];
  printf("%d",ans);
}

  

D. Diagram

判断是否存在$K$个数满足坐标模$\frac{N}{K}$的值相同即可。

#include <bits/stdc++.h>
using namespace std ;

const int MAXN = 100005 ;

map < int , int > mp ;

int a[MAXN] , n , k , l ;

void solve () {
	mp.clear () ;
	for ( int i = 0 ; i < n ; ++ i ) {
		scanf ( "%d" , &a[i] ) ;
	}
	scanf ( "%d" , &l ) ;
	l /= k ;
	for ( int i = 0 ; i < n ; ++ i ) {
		mp[a[i] % l] ++ ;
		if ( mp[a[i] % l] == k ) {
			printf ( "1\n" ) ;
			return ;
		}
	}
	printf ( "0\n" ) ;
}

int main () {
	while ( ~scanf ( "%d%d" , &n , &k ) ) solve () ;
	return 0 ;
}

  

E. Effective Hiring

考虑将所有人按能力从小到大排序,能力相同的按$2,3,1$的优先级排序,那么问题等价于在这个序列中选择$K$对左右括号,使得选出来的是个合法括号序列,dp即可,时间复杂度$O(NK\sqrt{NK})$。

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
typedef long long LL;
const int Maxn=100020;
const LL Inf=1LL<<60;
LL dp[2][334][334];
int n,k;
int a[Maxn],c[Maxn],w[Maxn],id[Maxn];//2->3,3->2
bool cmp(int x,int y){
	if(c[x]!=c[y])return c[x]<c[y];
	return a[x]>a[y];
}
void init(int cs){
	for(int i=0;i<=k;i++)for(int j=0;j<=k;j++)dp[cs][i][j]=Inf;
}
void up(LL &x,LL y){
	if(x>y)x=y;
}
int main(){
	while(scanf("%d%d",&n,&k)!=EOF){
		for(int i=1;i<=n;i++){
			id[i]=i;
			scanf("%d%d%d",a+i,c+i,w+i);
			if(a[i]==3)a[i]=2;
			else if(a[i]==2)a[i]=3;
		}
		sort(id+1,id+1+n,cmp);
		int cs=0;
		init(cs);dp[cs][0][0]=0;
		for(int i=1;i<=n;i++,cs^=1){
			int idx=id[i];
			//printf("idx=%d\n",idx);
			init(cs^1);
			for(int has=0;has<=k;has++){
				for(int j=0;j<=k;j++){
					if(dp[cs][has][j]==Inf)continue;
					//if(i==2&&has==0&&j==1)printf("val=%lld\n",dp[cs][has][j]);
					for(int it=1;it<=3;it+=2){
						if(a[idx]!=2&&a[idx]!=it)continue;
						int nhas=has+(it==1?1:0);
						int nj=j+(it==3?1:-1);
						if(nhas>k||nj>k||nj<0)continue;
						
					//if(i==2&&has==0&&j==1)printf("nhas=%d nj=%d\n",nhas,nj);
						up(dp[cs^1][nhas][nj],dp[cs][has][j]+w[idx]);
					}
					up(dp[cs^1][has][j],dp[cs][has][j]);
				}
			}
		}
		printf("%lld\n",dp[cs][k][0]);
	}
}

  

F. First And Last

高精度打表。

import java.io.*;
import java.math.*;
import java.util.*;

public class Main {
	static int [][]res=new int[11][11];
	public static void main(String[] args) {
		for(int i=0;i<10;i++)for(int j=0;j<10;j++)res[i][j]=-1;
		BigInteger o=BigInteger.ONE;
		for(int i=0;i<500;i++){
			int lst=o.mod(BigInteger.valueOf(10)).intValue();
			int fst=lst;
			BigInteger x=o;
			while(x.compareTo(BigInteger.ZERO)>0){
				fst=(x.mod(BigInteger.valueOf(10))).intValue();
				x=x.divide(BigInteger.TEN);
			}
			if(res[fst][lst]<0)res[fst][lst]=i;
			o=o.multiply(BigInteger.valueOf(2));
		}
		Scanner cin=new Scanner(System.in);
		while(cin.hasNext()){
			int a=cin.nextInt();
			int b=cin.nextInt();
			System.out.println(res[a][b]);
		}
		cin.close();
	}
}

  

G. Game of Solitaire

$ans=\gcd(n,k)$。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
typedef pair<ll,P>PI;
int n,K,i,j,a[30010][8];ll ans,lim=1e18;
priority_queue<PI,vector<PI>,greater<PI> >q;
inline void ext(int o,int x){
  ll t=0;
  for(int i=7;~i;i--){
    if(t>lim/x)return;
    t=t*x+a[o][i];
    if(t>lim)return;
  }
  q.push(PI(t,P(o,x)));
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
  scanf("%d%d",&n,&K);
  printf("%d",gcd(n,K));
}

  

H. Hero’s Quest

留坑。

 

I. Important Or Not?

首先将串翻转,并用Hash值对操作串进行去重,求出后缀数组,用线段树套set维护区间内重要的子串集合。

对于每个询问,在线段树上找到$O(\log n)$个set,在里面首先lower_bound出最短的满足条件的串,然后往后选取$k$个,最后nth_element即可。

时间复杂度$O(n\log^2n+nk\log n)$。

#include <bits/stdc++.h>
using namespace std ;

typedef pair < int , int > pii ;

#define clr( a , x ) memset ( a , x , sizeof a )
#define root 1 , 1 , n
#define ls o << 1
#define rs o << 1 | 1 
#define lson ls , l , m
#define rson rs , m + 1 , r

const int MAXN = 200005 ;
const int seed = 233 ;
const int P1 = 1e9 + 7 , P2 = 998244353 ;
const int LOGF = 20 ;

char s[MAXN] ;
int sa[MAXN] , rnk[MAXN] , height[MAXN] ;
int t1[MAXN] , t2[MAXN] , xy[MAXN] , c[MAXN] ;
int dp[MAXN][LOGF] , logn[MAXN] ;
set < pii > T[MAXN << 2] ;
set < pii > :: iterator it ;
map < pii , int > mp1 ;
map < pii , int > mp2 ;
map < pii , int > mp3 ;
int a[MAXN] , cnt ;
int p1[MAXN] , p2[MAXN] ;
int h1[MAXN] , h2[MAXN] ;
int n , q ;

int cmp ( int* r , int a , int b , int d ) {
	return r[a] == r[b] && r[a + d] == r[b + d] ;
}

void get_height ( int n , int k = 0 ) {
	for ( int i = 0 ; i <= n ; ++ i ) rnk[sa[i]] = i ;
	for ( int i = 0 ; i < n ; ++ i ) {
		if ( k ) -- k ;
		int j = sa[rnk[i] - 1] ;
		while ( s[i + k] == s[j + k] ) ++ k ;
		height[rnk[i]] = k ;
	}
}

void da ( int n , int m ) {
	int *x = t1 , *y = t2 ;
	for ( int i = 0 ; i < m ; ++ i ) c[i] = 0 ;
	for ( int i = 0 ; i < n ; ++ i ) c[x[i] = s[i]] ++ ;
	for ( int i = 1 ; i < m ; ++ i ) c[i] += c[i - 1] ;
	for ( int i = n - 1 ; i >= 0 ; -- i ) sa[-- c[x[i]]] = i ;
	for ( int d = 1 , p = 0 ; p < n ; d <<= 1 , m = p ) {
		p = 0 ;
		for ( int i = n - d ; i < n ; ++ i ) y[p ++] = i ;
		for ( int i = 0 ; i < n ; ++ i ) if ( sa[i] >= d ) y[p ++] = sa[i] - d ;
		for ( int i = 0 ; i < m ; ++ i ) c[i] = 0 ;
		for ( int i = 0 ; i < n ; ++ i ) c[xy[i] = x[y[i]]] ++ ;
		for ( int i = 1 ; i < m ; ++ i ) c[i] += c[i - 1] ;
		for ( int i = n - 1 ; i >= 0 ; -- i ) sa[-- c[xy[i]]] = y[i] ;
		swap ( x , y ) ;
		p = 0 ;
		x[sa[0]] = p ++ ;
		for ( int i = 1 ; i < n ; ++ i ) x[sa[i]] = cmp ( y , sa[i - 1] , sa[i] , d ) ? p - 1 : p ++ ;
	}
	get_height ( n - 1 ) ;
}

int rmq ( int L , int R ) {
	int k = logn[R - L + 1] ;
	return min ( dp[L][k] , dp[R - ( 1 << k ) + 1][k] ) ;
}

void init_rmq ( int n ) {
	for ( int i = 1 ; i <= n ; ++ i ) dp[i][0] = height[i] ;
	logn[1] = 0 ;
	for ( int i = 2 ; i <= n ; ++ i ) logn[i] = logn[i - 1] + ( i == ( i & -i ) ) ;
	for ( int j = 1 ; ( 1 << j ) < n ; ++ j ) {
		for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; ++ i ) {
			dp[i][j] = min ( dp[i][j - 1] , dp[i + ( 1 << ( j - 1 ) )][j - 1] ) ;
		}
	}
}

int get_L ( int x , int l , int r ) {
	int R = r ;
	while ( l < r ) {
		int m = l + r >> 1 ;
		if ( rmq ( m + 1 , R ) >= x ) r = m ;
		else l = m + 1 ;
	}
	return l ;
}

int get_R ( int x , int l , int r ) {
	int L = l ;
	while ( l < r ) {
		int m = l + r + 1 >> 1 ;
		if ( rmq ( L + 1 , m ) >= x ) l = m ;
		else r = m - 1 ;
	}
	return r ;
}

void build ( int o , int l , int r ) {
	T[o].clear () ;
	if ( l == r ) return ;
	int m = l + r >> 1 ;
	build ( lson ) ;
	build ( rson ) ;
}

void update ( int x , int p , int v , int o , int l , int r ) {
	pii tmp ( x , p ) ;
	if ( v ) T[o].insert ( pii ( x , p ) ) ;
	else if ( T[o].find ( tmp ) != T[o].end () ) T[o].erase ( tmp ) ;
	if ( l == r ) return ;
	int m = l + r >> 1 ;
	if ( p <= m ) update ( x , p , v , lson ) ;
	else update ( x , p , v , rson ) ;
}

void query ( int L , int R , int h , int k , int o , int l , int r ) {
	if ( L <= l && r <= R ) {
		it = T[o].lower_bound ( pii ( h , -1 ) ) ;
		for ( int i = 0 ; i < k && it != T[o].end () ; ++ it , ++ i ) {
			a[++ cnt] = it->first ;
		}
		return ;
	}
	int m = l + r >> 1 ;
	if ( L <= m ) query ( L , R , h , k , lson ) ;
	if ( m <  R ) query ( L , R , h , k , rson ) ;
}

pii get_Hash ( int L , int R ) {
	if ( L == 0 ) return pii ( h1[R] , h2[R] ) ;
	int v = R - L + 1 ;
	int v1 = ( h1[R] - 1LL * h1[L - 1] * p1[v] % P1 + P1 ) % P1 ;
	int v2 = ( h2[R] - 1LL * h2[L - 1] * p2[v] % P2 + P2 ) % P2 ;
	return pii ( v1 , v2 ) ;
}

void solve () {
	mp1.clear () ;
	mp2.clear () ;
	mp3.clear () ;
	n = strlen ( s ) ;
	reverse ( s , s + n ) ;
	h1[0] = s[0] ;
	h2[0] = s[0] ;
	for ( int i = 1 ; i < n ; ++ i ) {
		h1[i] = ( 1LL * h1[i - 1] * seed + s[i] ) % P1 ;
		h2[i] = ( 1LL * h2[i - 1] * seed + s[i] ) % P2 ;
	}
	da ( n + 1 , 128 ) ;
	init_rmq ( n ) ;
	build ( root ) ;
	//for ( int i = 1 ; i <= n ; ++ i ) {
	//	printf ( "%d " , sa[i] ) ;
	//}
	for ( int i = 0 ; i < q ; ++ i ) {
		int op , x , p , k ;
		scanf ( "%d%d%d" , &op , &x , &p ) ;
		pii tmp ( x , p ) ;
		p = n - p - 1 ;
		pii val = get_Hash ( p , p + x - 1 ) ;
		p = rnk[p] ;
		//printf ( "%d %d %d\n" , op , x , p ) ;
		if ( op == 1 ) {
			//if ( mp3[tmp] ) continue ;
			//mp3[tmp] = 1 ;
			if(mp1[val])continue;
			mp1[val] ++ ;
			if ( mp1[val] == 1 ) {
			        //puts("A");
				mp2[val] = p ;
				update ( x , p , 1 , root ) ;
			}
		} else if ( op == 2 ) {
			//if ( mp3[tmp] == 0 ) continue ;
			//mp3[tmp] = 0 ;
			if ( mp1[val] == 0 ) continue ;
			mp1[val] -- ;
			if ( mp1[val] == 0 ) {
			        //puts("B");
				update ( x , mp2[val] , 0 , root ) ;
			}
		} else {
			scanf ( "%d" , &k ) ;
			int L = get_L ( x , 1 , p ) ;
			int R = get_R ( x , p , n ) ;
			cnt = 0 ;
			query ( L , R , x , k , root ) ;
			nth_element ( a + 1 , a + k , a + cnt + 1 ) ;
			if ( cnt < k ) printf ( "-1\n" ) ;
			else printf ( "%d\n" , a[k] ) ;
		}
	}
}

int main () {
	p1[0] = p2[0] = 1 ;
	for ( int i = 1 ; i < MAXN ; ++ i ) {
		p1[i] = 1LL * p1[i - 1] * seed % P1 ;
		p2[i] = 1LL * p2[i - 1] * seed % P2 ;
	}
	while ( ~scanf ( "%s%d" , s , &q ) ) solve () ;
	return 0 ;
}

  

J. Joining Powers

二分答案,计数则考虑容斥,因为只关心选取集合的$lcm$以及奇偶性,而且$lcm>64$时与$64$无异,所以设$dp[i][j]$表示考虑了前$i$个集合,准备容斥的部分的$lcm$为$j$的容斥系数,求出系数之后开根号统计即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll inf=1e18;
const int MAGIC=7;
int n,m,cnt,i,j,a[55],b[55];
int lc[90][90];
ll f[20010][60];
ll l,r,mid,ans;
ll q[3000000];
int cur;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
inline ll cal2(ll a,int b){
  ll t=1;
  while(b--){
    if(t>inf/a)return inf;
    t*=a;
  }
  return t;
}
inline ll cal(ll a,int b){
  ll t=1;
  bool flag=0;
  while(b){
    if(b&1){
      if(flag)return inf;
      if(t>inf/a)return inf;
      t*=a;
    }
    b>>=1;
    if(a>inf/a)flag=1;
    if(!flag)a*=a;
  }
  return t;
}
inline ull ask(int k,ll lim){
  ll l=1,r=lim,mid,t=0;
  while(l<=r){
    mid=(l+r)>>1;
    if(cal(mid,k)<=lim)l=(t=mid)+1;else r=mid-1;
  }
  return t;
}
inline ull askfast(int k,ll lim){
  double t=pow(lim,1.0/k);
  ll x=(ll)(t);
  x=max(0LL,x);
  while(cal(x+1,k)<=lim)x++;
  while(x>0&&cal(x,k)>lim)x--;
  return 1ULL*x;
}
inline bool ispower(ll x,int k){
  ll l=1,r=x,mid;
  while(l<=r){
    mid=(l+r)>>1;
    ll t=cal(mid,k);
    if(t==x)return 1;
    if(t<x)l=mid+1;else r=mid-1;
  }
  return 0;
}
ull dp[55][70];
ull check(ll mid){
  int i,j,k;
  for(i=0;i<=m;i++)for(j=1;j<64;j++)dp[i][j]=0;
  dp[0][1]-=1;
  for(i=0;i<m;i++)for(j=1;j<64;j++)if(dp[i][j]){
    dp[i+1][j]+=dp[i][j];
    dp[i+1][min(63,lc[j][a[i+1]])]-=dp[i][j];
  }
  ull ret=0;
  for(i=2;i<64;i++)if(dp[m][i]){
    //printf("%d %llu\n",i,dp[m][i]);
    ret+=dp[m][i]*askfast(i,mid);
  }
  /*cur=0;
  for(i=0;i<m;i++)if(a[i]>MAGIC){
    for(j=1;;j++){
      ll t=f[j][a[i]];
      if(t>mid)break;
      q[++cur]=t;
    }
  }
  sort(q+1,q+cur+1);
  ull ret=0;
  for(i=1;i<=cur;i++)if(i==1||q[i]!=q[i-1]){
    for(k=0;k<cnt;k++)if(ispower(q[i],b[k]))break;
    if(k==cnt)ret++;
  }
  int S;
  for(S=1;S<1<<cnt;S++){
    int o=1;
    for(i=0;i<cnt;i++)if(S>>i&1)o=lcm(o,b[i]);
    if(__builtin_popcount(S)&1)ret+=ask(o,mid);else ret-=ask(o,mid);
  }*/
  return ret;
}
ll solve(){
  scanf("%d%d",&n,&m);
  cnt=0;
  for(i=1;i<=m;i++){
    scanf("%d",&a[i]);
    //if(a[i]<=MAGIC)b[cnt++]=a[i];
  }
  for(i=1;i<=m;i++)if(a[i]==1)return n;
  l=1,r=100000000000000009LL;
  while(l<r){
    mid=(l+r)>>1;
    if(check(mid)>=n)r=mid;else l=mid+1;
  }
  return l;
}
int main(){
  int T;
  for(i=1;i<90;i++)for(j=1;j<90;j++)lc[i][j]=lcm(i,j);
  //for(i=1;i<=20000;i++)for(j=1;j<=50;j++)f[i][j]=cal(i,j);
  for(scanf("%d",&T);T--;printf("%lld\n",solve()));
  return 0;
}

  

K. Keyboard Map

$f[i][j]$表示前$j$个数划分成$i$段的最小花费,转移显然具有决策单调性,分治求解即可。时间复杂度$O(nm\log n)$。

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
typedef long long LL;
const int Maxn=1000020;
const LL Inf=1LL<<60;
LL sum1[5050],sum2[5050];
LL a[5050];
int n,k;
LL f[3030][5050];
int O;
LL cal(int l,int r){
	return sum1[r]-sum1[l]-(sum2[r]-sum2[l])*l;
}
void solve(int l,int r,int dl,int dr){
  int m=(l+r)>>1,dm=dl;
  LL&t=f[O][m];
  t=Inf;
  for(int i=dl;i<=dr&&i<m;i++){
    LL now=f[O-1][i]+cal(i,m);
    if(now<t)t=now,dm=i;
  }
  if(l<m)solve(l,m-1,dl,dm);
  if(r>m)solve(m+1,r,dm,dr);
}
int main(){
	while(scanf("%d%d",&n,&k)!=EOF){
		for(int i=1;i<=n;i++){
			scanf("%lld",a+i);
			sum1[i]=sum1[i-1]+i*a[i];
			sum2[i]=sum2[i-1]+a[i];
		}
		//printf("cal33=%lld\n",cal(2,3));
		for(int j=0;j<=n;j++)
				f[1][j]=sum1[j];
		for(O=2;O<=k;O++){
		  solve(O,n,O-1,n);
                }
			/*	else{
					f[i][j]=Inf;
					for(int bef=0;bef<j;bef++){
						f[i][j]=min(f[i][j],f[i-1][bef]+cal(bef,j));
					}
				}
			}
		}*/
		//printf("f23=%lld\n",f[2][3]);
		printf("%lld\n",f[k][n]);
	}
}

  

L. Light Sources

显然当选出来的多边形是个凸包时,周长最小。

枚举起点,然后极角排序,设$dp[i][j]$表示选取的最后一个点是$i$,选中的颜色集合为$j$的最小周长,然后枚举下一个凸包上的点转移即可。

时间复杂度$O(m^32^k)$。

#include<bits/stdc++.h>
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 305 ;

struct P {
	LL x , y ;
	P () {}
	P ( LL x , LL y ) : x ( x ) , y ( y ) {}
	LL operator * ( const P& p ) const {
		return x * p.y - y * p.x ;
	}
	P operator + ( const P& p ) const {
		return P ( x + p.x , y + p.y ) ;
	}
	P operator - ( const P& p ) const {
		return P ( x - p.x , y - p.y ) ;
	}
	bool operator < ( const P& p ) const {
		return x != p.x ? x < p.x : y < p.y ;
	}
	double len () {
		return sqrt ( x * x + y * y ) ;
	}
	double angle () {
		return atan2 ( y , x ) ;
	}
} ;

struct Node {
	double r ;
	int idx ;
	bool operator < ( const Node& a ) const {
		return r < a.r ;
	}
} ;

P p[MAXN] , q[MAXN] ;
double len[MAXN][MAXN] ;
double dp[MAXN][1 << 7] ;
int val[MAXN][MAXN] ;
Node a[MAXN] ;
int c[MAXN] ;
double ans ;
int n , m , K ;

int dcmp ( LL x ) {
	if ( x ) return x > 0 ? 1 : -1 ;
	return 0 ;
}

bool PointInTri ( P& i , P& j , P& k , P& l ) {
	int a = dcmp ( ( i - l ) * ( j - l ) ) ;
	int b = dcmp ( ( j - l ) * ( k - l ) ) ;
	int c = dcmp ( ( k - l ) * ( i - l ) ) ;
	return a * b > 0 && b * c > 0 && c * a > 0 ;
}

void calc ( int m ) {
	int tot = 1 << K ;
	for ( int i = 0 ; i <= m ; ++ i ) {
		for ( int j = 0 ; j < tot ; ++ j ) {
			dp[i][j] = 1e60 ;
		}
		for ( int j = 0 ; j <= m ; ++ j ) {
			len[i][j] = ( p[a[i].idx] - p[a[j].idx] ).len () ;
		}
	}
	for ( int i = 1 ; i <= m ; ++ i ) {
		for ( int j = i + 1 ; j <= m ; ++ j ) {
			val[i][j] = 0 ;
			for ( int l = 0 ; l < n ; ++ l ) {
				if ( PointInTri ( p[a[0].idx] , p[a[i].idx] , p[a[j].idx] , q[l] ) ) {
					val[i][j] |= 1 << c[l] ;
				}
			}
		}
	}
	for ( int i = 1 ; i <= m ; ++ i ) {
		dp[i][0] = len[0][i] * 2 ;
		for ( int s = 0 ; s < tot ; ++ s ) {
			for ( int k = 1 ; k < i ; ++ k ) if ( dp[k][s] < 1e50 ) {
				int nxt = s | val[k][i] ;
				dp[i][nxt] = min ( dp[i][nxt] , dp[k][s] + len[0][i] + len[k][i] - len[0][k] ) ;
			}
		}
		ans = min ( ans , dp[i][tot - 1] ) ;
	}
}

void solve () {
	for ( int i = 0 ; i < n ; ++ i ) {
		scanf ( "%lld%lld" , &q[i].x , &q[i].y ) ;
	}
	for ( int i = 0 ; i < n ; ++ i ) {
		scanf ( "%d" , &c[i] ) ;
		c[i] -- ;
	}
	for ( int i = 1 ; i <= m ; ++ i ) {
		scanf ( "%lld%lld" , &p[i].x , &p[i].y ) ;
	}
	ans = 1e60 ;
	sort ( p + 1 , p + m + 1 ) ;
	for ( int i = 1 ; i <= m ; ++ i ) {
		int cnt = 0 ;
		for ( int j = i + 1 ; j <= m ; ++ j ) {
			++ cnt ;
			a[cnt].idx = j ;
			a[cnt].r = ( p[j] - p[i] ).angle () ;
		}
		a[0].idx = i ;
		sort ( a + 1 , a + cnt + 1 ) ;
		calc ( cnt ) ;
	}
	if ( ans < 1e50 ) printf ( "%.12f\n" , ans ) ;
	else printf ( "-1\n" ) ;
}

int main () {
	while ( ~scanf ( "%d%d%d" , &n , &m , &K ) ) solve () ;
	return 0 ;
}

  

M. Merging

不断用堆取出最小的$k$项即可。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
typedef pair<ll,P>PI;
int n,K,i,j,a[30010][8];ll ans,lim=1e18;
priority_queue<PI,vector<PI>,greater<PI> >q;
inline void ext(int o,int x){
  ll t=0;
  for(int i=7;~i;i--){
    if(t>lim/x)return;
    t=t*x+a[o][i];
    if(t>lim)return;
  }
  q.push(PI(t,P(o,x)));
}
int main(){
  scanf("%d",&n);
  for(i=1;i<=n;i++)for(j=7;~j;j--)scanf("%d",&a[i][j]);
  scanf("%d",&K);
  for(i=1;i<=n;i++)ext(i,1);
  while(K--){
    PI t=q.top();q.pop();
    ans=t.first;
    ext(t.second.first,t.second.second+1);
  }
  printf("%lld",ans);
}

  


总结:

  • J题对一开始的算法时间复杂度分析错误,致使4次TLE。
  • F题偷懒用__int128结果WA,下次要注意。

 

posted @ 2016-11-17 20:32 Claris 阅读(...) 评论(...) 编辑 收藏