test20190815

闲话

这两天考试真是妙啊,打暴力都有200分qwq。

题面

题面

T1

Solution

因为是求立方之差,可以考虑立方差公式。

((a-b)^3=(a-b) imes(a^2+a imes b+b^2))

又因为(P)是一个质数,所以((a-b))((a^2+a imes b+b^2))里面一定有一个是1。明显,((a^2+a imes b+b^2))一定大于1,所以可得到((a-b))的值为1,也就是说,(P)一定是相邻两个数的立方差。

我们可以循环找一下,可以发现,当(k=577351)时,(k^3-(k-1)^3)的值第一次大于(10^{12})。所以直接从1开始,一直枚举到找到答案或者差值大于(P)结束即可。

时间复杂度(O(N imes 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
");
		else printf("NO
");
	}
	return 0;
}

T2

Solution

(70pts)做法:

对于(60)%的数据,(N<=1000,k<=20)

很显然(O(N^2 imes 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 imes(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
", ans);
	return 0;
}

总结

考完后进行交流大佬讲题的时候,发现自己还是好弱啊,说的东西都没听懂。。。

还是要好好加油啊。

原文地址:https://www.cnblogs.com/TheShadow/p/11360850.html