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