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,下次要注意。