刷省选题(2/100)
P6163 [Cnoi2020]领域极限(三分)
昨天用了一个很难的贪心证明去写了,虽然时间复杂度和空间复杂度都是最优的,但是证明非常的难,也很难用那种神奇的角度去思考,所以今天用三分来写一下
首先我们可以看出题目中给出的就是一个公式,也可以说这是一个函数,那么我们选择的数上届显然就是所有区间里r的最大值,下界就是所有区间里l的最小值,那么在这个范围内,函数的值其实呈现出了凹函数的特性,所以我们可以利用三分法来求的极小值。
#include
using namespace std;
#define int int
const int N=1e5+5;
int n;
int x[N],y[N],a[N];
int pre[N],suf[N];
int calc(int t)
{
int res=0;
for(int i=1;i<=n;++i)
{
if(y[i]<t) a[i]=y[i];
else if(x[i]>t) a[i]=x[i];
else a[i]=t;
}
sort(a+1,a+n+1);
for(int i=1;i<=n;++i) pre[i]=pre[i-1]+a[i];
for(int i=n;i>=1;--i) suf[i]=suf[i+1]+a[i];
for(int i=1;i<=n;++i) res+=a[i]*i-pre[i]+suf[i]-a[i]*(n-i+1);
return res;
}
int main()
{
int n;
cin>>n;
int l=1e9,r=0;
for(int i=1;i<=n;++i)
{
cin>>x[i]>>y[i];
l=min(l,x[i]),r=max(r,y[i]);
}
while(l<=r)
{
int lmid=l+(r-l)/3,rmid=r-(r-l)/3;
if(calc(lmid)<calc(rmid)) r=rmid-1;
else l=lmid+1;
}
cout<<calc(r)<<endl;
return 0;
}
The 2019 ICPC Asia Nanjing Regional Contest
A题
题意:给一个长度为n个排列,要求你确定一个大小k,使得从这个排列中挑出任意一个大小为k的子集都存在一个二元组(u,v)
使得u是v的因子
思路:我们知道相邻的两个数互质,根据这个规则,我们如果拿出一个序列想要让里面的所有数互质,那最多只能拿走前一半或者后一半,所以只要多拿走一个让他不互质就行了。
#include
using namespace std;
const int N = 1e6+100;
#define endl '\n'
int a[N];
signed main()
{
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t;
for(cin>>t;t;t--)
{
int n;
cin>>n;
if(n%2)
{
cout<<n/2+2<<endl;
}
else
{
cout<<n/2+1<<endl;
}
}
return 0;
}
C(DP+记忆化搜索)
题意:给定一个二维数组,我们要求出数组中“路径”的个数,路径的定义是:一些由相邻格子组成,格子内元素有序递增且不可再拓展长度而且长度最少为4的连续格子
思路:我们设dp状态为dp[i][j][k]代表,第(i,j)这个格子作为倒数第k个路径中的格子,这样的路径条数有多少。
至于为什么用倒数第k个,主要是因为避免数组过大,因为题目中说长度至少为4,那么k的取值最大就是4,dp状态也是很好更新的,我们要去下一个格子的时候先看看下个格子是否被搜过,是的话先更新,否则先搜索再更新。
和平时的记忆化搜索不太一样的是内部还含有递推的过程
#include
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\n'
using namespace std;
const int N = 1e3+100;
const int mod = 1e9+7;
int vis[1005][1005],dp[1005][1005][5],a[1005][1005],n,m;
int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};
bool check(int x,int y)
{
for(int i=0;i<4;i++)
{
int tx=x+dx[i],ty=y+dy[i];
if(tx<1||tx>n||ty<1||ty>m) continue;
if(a[tx][ty]+1==a[x][y]) return 0;
}
return 1;
}
void dfs(int x1,int x2,int x3,int x4)
{
vis[x1][x2]=1;
int flag=0;
for(int i=0;i<4;i++)
{
int tx=x1+dx[i],ty=x2+dy[i];
if(tx<1||tx>n||ty<1||ty>m) continue;
if(a[tx][ty]-1==a[x1][x2])
{
flag=1;
if(vis[tx][ty])
{
dp[x1][x2][2]=(dp[x1][x2][2]+dp[tx][ty][1])%mod;
dp[x1][x2][3]=(dp[x1][x2][3]+dp[tx][ty][2])%mod;
dp[x1][x2][4]=((dp[tx][ty][4]+dp[tx][ty][3])%mod+dp[x1][x2][4]%mod)%mod;
continue;
}
dfs(tx,ty,x1,x2);
dp[x1][x2][2]=(dp[x1][x2][2]+dp[tx][ty][1])%mod;
dp[x1][x2][3]=(dp[x1][x2][3]+dp[tx][ty][2])%mod;
dp[x1][x2][4]=((dp[tx][ty][4]+dp[tx][ty][3])%mod+dp[x1][x2][4]%mod)%mod;
}
}
if(!flag) dp[x1][x2][1]=1;
return;
}
signed main()
{
ios;
memset(dp,0,sizeof(dp));
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(!vis[i][j]) dfs(i,j,0,0);
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(check(i,j)) {
ans=(ans+dp[i][j][4])%mod;
}
cout<<ans<<endl;
return 0;
}
G(思维,博弈)
题意:有a个说真话的,b个说假话的,c个有可能说真话也有可能说假话的。确保a至少有一个人。
你需要问这些人的问题是,公主是谁(公主是a个人中的一个)
假设提前知道a,b,c的数量,请求出最小几次才能知道公主是谁。
整个题应该是不难的,思路很好想,但是有个特判,就是当只有一个a的时候,其他都没有,我们一次都不需要询问,因为我们提前知道有多少人同意,所以直接就能猜出公主就是那个唯一的a
#include
using namespace std;
const int N = 1e6+100;
#define endl '\n'
int a[N];
signed main()
{
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int a,b,c;
cin>>a>>b>>c;
if(a==1&&b==0&&c==0)
{
cout<<"YES"<<endl;
cout<<0<<endl;return 0;
}
if(a>b+c)
{
cout<<"YES"<<endl;
cout<<2*(b+c)+1<<endl;
}
else
{
cout<<"NO"<<endl;
}
return 0;
}