test20190815
闲话
这两天考试真是妙啊,打暴力都有200分qwq。
题面
T1
Solution
因为是求立方之差,可以考虑立方差公式。
\((a-b)^3=(a-b)\times(a^2+a\times b+b^2)\)
又因为\(P\)是一个质数,所以\((a-b)\)和\((a^2+a\times b+b^2)\)里面一定有一个是1。明显,\((a^2+a\times b+b^2)\)一定大于1,所以可得到\((a-b)\)的值为1,也就是说,\(P\)一定是相邻两个数的立方差。
我们可以循环找一下,可以发现,当\(k=577351\)时,\(k^3-(k-1)^3\)的值第一次大于\(10^{12}\)。所以直接从1开始,一直枚举到找到答案或者差值大于\(P\)结束即可。
时间复杂度\(O(N\times MAXK)\)
Code
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il print(T x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x%mod;
while(m){
if(m&1) res=(res*bas)%mod;
bas=(bas*bas)%mod,m>>=1;
}
return res%mod;
}
const int MAXN = 577351,maxn = 578;
ll T,p;
bool check(ll p){
for(ri i=1;i<=MAXN;++i){
rl mns=1ll*i*i*i-1ll*(i-1)*(i-1)*(i-1);
if(mns==p)
return true;
else if(mns>p) return false;
}
return false;
}
int main()
{
freopen("cubicp.in","r",stdin);
freopen("cubicp.out","w",stdout);
read(T);
while(T--){
read(p);
if(check(p)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
T2
Solution
\(70pts\)做法:
对于\(60\)%的数据,\(N<=1000,k<=20\)。
很显然\(O(N^2\times k)\)的算法是可以跑过的。
考虑DP题目名称不就是吗
我们定义\(DP_{i,j}\)表示到达第 \(i\) 个数,分成 \(j\) 段所得到的最小值。
方程转移也很简单。
\(DP_{i,j}=Min(DP_{s,j-1}+val_{s+1,i})\)
我们只用预处理出\(val_{i,j}\)就可以进行直接转移。
我们再看数据范围,在每一档里面都有一个点,所有的\(a_i\)完全相同。
我们简单分析一下,可以发现,当我们有 \(k\) 个相同值时,\(k\) 越大,它的贡献和 \(k-1\) 相差就越大,所以我们要将这 \(N\) 个数尽可能的均分为 \(k\) 份。
如果 \(N\) 能被 \(k\) 整除,那么每一段的大小都是 \(N/k\) ;如果不能整除,那么多出来的 \(rest\) 个数,将它们平分到 \(rest\) 个区间中。
每一段区间对答案的贡献很好计算,\(val=\frac{size\times(size-1)}{2}\)
然后直接乘上个数就是答案。
Code
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il print(T x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x%mod;
while(m){
if(m&1) res=(res*bas)%mod;
bas=(bas*bas)%mod,m>>=1;
}
return res%mod;
}
const int MAXN = 1e3+1;
int n,k,a[MAXN],val[MAXN][MAXN],w,dp[21][MAXN],cnt[MAXN][MAXN],last[MAXN];
int main()
{
freopen("dp.in","r",stdin);
freopen("dp.out","w",stdout);
read(n),read(k),del(dp,0x3f);
if(n<=1000){
for(ri i=1;i<=n;++i) read(a[i]);
for(ri i=1;i<=n;++i){
val[1][i]=val[1][i-1]+cnt[a[i]][last[a[i]]];
cnt[a[i]][i]=cnt[a[i]][last[a[i]]]+1,last[a[i]]=i;
}
for(ri i=2;i<=n;++i){
ri mns=0;
for(ri j=i;j<=n;++j){
if(a[j]==a[i-1]) mns++;
val[i][j]=val[i-1][j]-mns;
}
}
dp[0][0]=0;
for(ri s=1;s<=k;++s)
for(ri i=1;i<=n;++i)
for(ri j=0;j<i;++j)
dp[s][i]=min(dp[s][i],dp[s-1][j]+val[j+1][i]);
printf("%d",dp[k][n]);
}
else{
for(ri i=1;i<=n;++i) read(w);
ri add=n%k,len=n/k;
printf("%d",(k-add)*len*(len-1)/2+add*(len+1)*len/2);
}
return 0;
}
\(100\)pts做法:
令 \(DP_{i,j}\) 表示前 \(i\) 个数分成 \(j\) 段的最少价值。枚举这个断点 \(k\) ,有 \(DP_{i,j}\) = \(Min(DP_{k,j-1}+sum_{k+1,i})\) 。事实上这个 \(k\) 具有单调性,用经典的1D1D动态规划优化即可。什么东西啊(雾
Code
#include<iostream>
#include<cstdio>
using namespace std;
const int N=100010;
typedef long long LL;
int c[N],a[N];
LL f[N],g[N];
int p,q,n,k;
LL tot;
void move(int l,int r)
{
while (l<p) p--,tot+=c[a[p]],c[a[p]]++;
while (r>q) q++,tot+=c[a[q]],c[a[q]]++;
while (p<l) c[a[p]]--,tot-=c[a[p]],p++;
while (r<q) c[a[q]]--,tot-=c[a[q]],q--;
}
void work(int l,int r,int fl,int fr)
{
if (fl>fr) return;
int mid=(fl+fr)>>1,mi;
LL mx=1LL<<60;
for (int i=l;i<=r;i++)
if (i<mid)
{
move(i+1,mid);
if (f[i]+tot<mx) mx=f[i]+tot,mi=i;
}
g[mid]=mx;
work(l,mi,fl,mid-1);
work(mi,r,mid+1,fr);
}
int main()
{
freopen("dp.in","r",stdin);
freopen("dp.out","w",stdout);
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
f[0]=0;
for (int i=1;i<=n;i++) f[i]=1LL<<60;
while (k--)
{
p=1,q=0,tot=0;
for (int i=1;i<=n;i++) c[i]=0;
work(0,n-1,1,n);
for (int i=0;i<=n;i++) f[i]=g[i],g[i]=0;
}
cout<<f[n];
return 0;
}
T3
Solution
对于所有区间最小值为x的操作的区间交只有一种情况会出现矛盾:对于最小值超过x的区间的并包含该区间交。
因此我们可以二分答案,之后将所有最小值从大到小进行排序,利用并查集维护即可。没看懂。。。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 1000011
#define min(x, y) ((x) < (y) ? (x) : (y))
#define max(x, y) ((x) > (y) ? (x) : (y))
using namespace std;
int n, q, ans;
int f[N];
struct node
{
int x, y, z;
}p[N], t[N];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline bool cmp(node x, node y)
{
return x.z > y.z;
}
inline int find(int x)
{
return x == f[x] ? x : f[x] = find(f[x]);
}
inline bool check(int k)
{
int i, j, x, y, lmin, lmax, rmin, rmax;
for(i = 1; i <= n + 1; i++) f[i] = i;
for(i = 1; i <= k; i++) t[i] = p[i];
std::sort(t + 1, t + k + 1, cmp);
lmin = lmax = t[1].x;
rmin = rmax = t[1].y;
for(i = 2; i <= k; i++)
{
if(t[i].z < t[i - 1].z)
{
if(find(lmax) > rmin) return 1;
for(j = find(lmin); j <= rmax; j++)
f[find(j)] = find(rmax + 1);
lmin = lmax = t[i].x;
rmin = rmax = t[i].y;
}
else
{
lmin = min(lmin, t[i].x);
lmax = max(lmax, t[i].x);
rmin = min(rmin, t[i].y);
rmax = max(rmax, t[i].y);
if(lmax > rmin) return 1;
}
}
// cout<<find(1)<<endl;
if(find(lmax) > rmin) return 1;
return 0;
}
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
int i, x, y, mid;
n = read();
q = read();
for(i = 1; i <= q; i++)
p[i].x = read(), p[i].y = read(), p[i].z = read();
x = 1, y = q;
//cout<<check(2)<<endl;
//return 0;
while(x <= y)
{
mid = (x + y) >> 1;
if(check(mid)) ans = mid, y = mid - 1;
else x = mid + 1;
}
printf("%d\n", ans);
return 0;
}
总结
考完后进行交流大佬讲题的时候,发现自己还是好弱啊,说的东西都没听懂。。。
还是要好好加油啊。