校内集训20181003
$T1$:
现在给你$N+1$个数,其中有$N$个数是合法的,合法的定义是$A_i=x\times p_i$,其中$x$为固定合数,$p_i$为两两互异的质数。
还有一个数是不合法的,不合法即要么不满足除$x$的商是一个质数,要么不整除$x$,现在请找出那个不合法的数。
$N\leq10^5,x\leq10^5,p_i\leq1.5\times10^6$
$Solution$:
如果所有合法的数都是形如一个大合数乘一个素数的,那我们直接从$A$里随便取三个数两两求$gcd$,出现两次及以上的不就是$x$了?
既然这样,那我为什么一开始想了一堆$O(log^2)$的奇形怪状的方法强行耽误时间……
(正北方的刘某同学写了表筛$LLE$(代码长度超限)了,堪称千古绝唱)
$Code$:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define MAXN 100005 #define MAXM 1500005 #define INF 0x7fffffff #define ll long long ll A[MAXN],p[MAXM],isp[MAXM],cnt; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void init(ll x){ memset(isp,-1,sizeof(isp)); for(ll i=2;i<=x;i++){ //cout<<i<<endl; if(isp[i]==-1) p[++cnt]=i,isp[i]=1; for(ll j=1;j<=cnt;j++){ if(i*p[j]>x) break; isp[i*p[j]]=0; if(i%p[j]==0) break; } } return; } int main(){ //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); init(MAXM-5); ll N=read(),num=0,maxm=0,x=-1; bool flag=0; for(ll i=1;i<=N+1;i++) A[i]=read(); for(ll k=1;k<=MAXN;k++){ bool tp1=0;ll tp2=0; for(ll i=1;i<=N+1;i++){ if(A[i]/k>MAXM-5) {if(tp1) break;else tp1=1;} else if(!isp[A[i]/k] || A[i]==k || A[i]%k!=0) {if(tp1) break;else tp1=1;} else tp2++;if(tp2==3) {x=k;flag=1;break;} } if(flag) break; } for(ll i=1;i<=N+1;i++) if(A[i]/x>MAXM-5 || !isp[A[i]/x] || A[i]%x!=0) {printf("%lld\n",A[i]);break;} return 0; }
$T2$:
给你$N$块密度均匀且宽,高相等的积木坐标$l_i,r_i$,现在需要你把这$N$块积木按序分成任意座积木塔,
你需要保证把所有积木塔从低到高搭起来时不会出现不稳定结构,问含积木块数最多的积木塔最少可以含多少块积木。
稳定的定义:
若一个积木塔有$N$层,当且仅当对于$∀i\in[1,N)$,从上至下数,前$i$块积木的重心落在第$i+1$块积木的覆盖范围内,
则稳定,否则不稳定。前$i$块积木的重心为前$i$块积木的几何中心,以质量为权重的带权平均位置。
$N\leq10^3,l_i,r_i\leq10^4$
$Solution$:
这题最难的点在于知道带权平均数的计算方式为
$$ave=\frac {\sum_{i=1}^{n} {w_i \times p_i}} {\sum_{i=1}^{n} {w_i}}$$
然后就没难点了……设$dp(i)$表示从下到上处理到第$i$块积木的答案,
预处理$\{A_i...A_j\}$是否稳定后枚举最后一座塔的形态即可实现$O(N^2)$转移。
$Code$:(手懒贴了wls巨佬的)
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<algorithm> #define ll long long using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } int n; double L[1005],R[1005]; double g[1005][1005],sz[1005][1005]; bool vis[1005][1005]; int dp[1005]; int main() { n=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) vis[i][j]=1; for(int i=1;i<=n;i++) { scanf("%lf%lf",&L[i],&R[i]); g[i][i]=(R[i]+L[i])/2;sz[i][i]=(R[i]-L[i]); vis[i][i]=1; } for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) { sz[i][j]=sz[i][j-1]+R[j]-L[j]; g[i][j]=(g[i][j-1]*sz[i][j-1]+g[j][j]*sz[j][j])/sz[i][j]; vis[i][j]=vis[i][j-1]; if(!(g[i][j-1]>=L[j]&&g[i][j-1]<=R[j])) vis[i][j]=0; } } memset(dp,27,sizeof(dp)); dp[n+1]=0; for(int i=n;i>=1;i--) { for(int j=i;j<=n;j++) { if(!vis[i][j]) break; if(!vis[i][n]) continue; if(j==n||(g[i][j]>=L[j+1]&&g[i][j]<=R[j+1])) dp[i]=min(dp[i],max(dp[j+1],j-i+1)); } } printf("%d\n",dp[1]); }
$T3$:
两列火车第一列$na$节车厢,第二列$nb$节车厢,每个车厢里有若干个$\{1...N\}$之间的整数,每个整数在两列车里出现且仅出现一次。
现在你需要按某种顺序排列车厢,然后把第一列车的车厢中的数按顺序取出扔到栈1,第二列扔到栈2。
再对栈1栈2进行$k$次操作,每次操作弹出一个栈顶放到另一个栈顶。$k$次操作后$\{1...N\}$每个数都要在栈1顶出现过一次。
问如何排列车厢使得$k$最小。
$N\leq10^5,na,nb\leq20$
$Solution$:
看了lv神的题解还是只会写暴力啊……
暴力枚举车厢的排列顺序然后计算每种排列顺序的答案(这句话极其难以实现)后取$min$。
正解大概是预处理出同一列车内车厢间连线数,车厢内连线距离,车厢到对侧列车连线数后状压$DP$。
$Code$:(同上std)
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define MAXN 100010 #define MAXH 20 using namespace std; const int Size=1<<16; char buffer[Size],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,Size,stdin); tail=(head=buffer)+l; } if(head==tail) return -1; return *head++; } inline int read() { int x=0,t=1,c; while(!isdigit(c=Getchar()))if(c=='-')t=-1; while(isdigit(c))x=x*10+c-'0',c=Getchar(); return x*t; } int N,n[2]; int k[2][MAXH],s[2]; int side[MAXN],block[MAXN],pos[MAXN]; long long opposite[2][MAXH]; long long link[2][MAXH][MAXH]; long long inside[2][MAXH]; long long cross[2][MAXH][MAXH]; long long f[2][1<<MAXH]; long long cf[2][1<<MAXH]; long long csum[2][MAXN]; long long lsum[MAXH][1<<MAXH]; int LOG[1<<MAXH]; int base; int rev(int x){return (~x)&base;} int lowbit(int x){return x&-x;} void GetLinkSum(int side,int j,int x) { int revx=rev(x),key=lowbit(revx),from=revx-key; lsum[j][x]=lsum[j][rev(from)]+link[side][j][LOG[key]]; } long long TakeMin(long long &a,long long b) { return a=min(a,b); } void Update(int side,int state) { long long Remain=0; for(int i=0;i<n[side];i++) { bool hasi=state&(1<<i); if(!hasi)Remain+=k[side][i]; } for(int i=0;i<n[side];i++) { int newstate=state|(1<<i); long long Raise=0,Cro; if(!(state&(1<<i))) { Raise=inside[side][i]+opposite[side][i]*(Remain-k[side][i]); Raise+=lsum[i][newstate]; Cro=(cf[side][state]+cf[side][newstate]-csum[side][i])/2; Raise+=Cro*k[side][i]; TakeMin(f[side][newstate],f[side][state]+Raise); } } } void BuildTransdata(int side,int x) { int key=lowbit(x),from_state=x-key; long long ret=cf[side][from_state]; for(int i=0;i<n[side];i++) { if((1<<i)&key) { key=i; break; } } for(int i=0;i<n[side];i++) { if(i==key)continue; if((1<<i)&x) { ret-=cross[side][i][key]; } else { ret+=cross[side][i][key]; } } cf[side][x]=ret; } int main() { N=read();n[0]=read();n[1]=read(); for(int i=0;i<n[0];i++) { k[0][i]=read(); s[0]+=k[0][i]; for(int j=1;j<=k[0][i];j++) { int node=read(); side[node]=0; block[node]=i; pos[node]=j; } } for(int i=0;i<n[1];i++) { k[1][i]=read(); s[1]+=k[1][i]; for(int j=1;j<=k[1][i];j++) { int node=read(); side[node]=1; block[node]=i; pos[node]=j; } } opposite[side[1]][block[1]]++; inside[side[1]][block[1]]+=k[side[1]][block[1]]-pos[1]+side[1]; for(int x=1;x<N;x++) { int y=x+1; if(side[x]!=side[y]) { opposite[side[x]][block[x]]++; opposite[side[y]][block[y]]++; inside[side[x]][block[x]]+=k[side[x]][block[x]]-pos[x]+side[x]; inside[side[y]][block[y]]+=k[side[y]][block[y]]-pos[y]+side[y]; } else { if(block[x]==block[y]) { inside[side[x]][block[x]]+=abs(pos[x]-pos[y]); } else { cross[side[x]][block[x]][block[y]]++; cross[side[x]][block[y]][block[x]]++; link[side[x]][block[x]][block[y]]+=k[side[x]][block[x]]-pos[x]+pos[y]; link[side[x]][block[y]][block[x]]+=k[side[x]][block[y]]-pos[y]+pos[x]; } } } cf[0][0]=0; cf[1][0]=0; for(int i=1;i<(1<<n[0]);i++)BuildTransdata(0,i); for(int i=1;i<(1<<n[1]);i++)BuildTransdata(1,i); for(int i=0;i<n[0];i++) for(int j=0;j<n[0];j++) csum[0][i]+=cross[0][i][j]; for(int i=0;i<n[1];i++) for(int j=0;j<n[1];j++) csum[1][i]+=cross[1][i][j]; memset(f,127,sizeof f); f[0][0]=f[1][0]=0; for(int i=2;i<(1<<n[0]);i++)LOG[i]=LOG[i>>1]+1; base=(1<<n[0])-1; for(int i=0;i<n[0];i++)lsum[i][base]=0; for(int i=0;i<n[0];i++) for(int j=base-1;j>=0;j--) GetLinkSum(0,i,j); for(int i=0;i<(1<<n[0])-1;i++) Update(0,i); for(int i=2;i<(1<<n[1]);i++)LOG[i]=LOG[i>>1]+1; base=(1<<n[1])-1; for(int i=0;i<n[1];i++)lsum[i][base]=0; for(int i=0;i<n[1];i++) for(int j=base-1;j>=0;j--) GetLinkSum(1,i,j); for(int i=0;i<(1<<n[1])-1;i++) Update(1,i); printf("%lld\n",f[0][(1<<n[0])-1]+f[1][(1<<n[1])-1]); }