「EZEC」 Round 10 庆典 div3
「EZEC」 Round 10 庆典 div3
「EZEC-10」打分(题目)
题目大意:给出一个序列\(a_1,a_2...a_n\),可以操作\(m\)次,每次使\(a_i+1(1\leqslant i \leqslant n)\),问去掉一个最大值一个最小值之后和的最大值
分析:
- 我们可以将原序列排序,因为可以去掉最小值,所以说\(a_1\)我们可以不管,剩下就是要操作\(m\)次,使得去掉最大值后的和尽可能大
- 那么我们其实就要使得\(a_2-a_{n-1}\)尽可能大,但\(不能大于a_n\),不然\(a_n\)就不是被舍掉的了,如果每个数都与\(a_n\)相等了,但\(m\)还没操作完,就可以将\(a_n+1\)这样就有了更大的上限,重复操作到\(m\)次即可
- 其实可以把这些操作转换成算式的,如果没有使每个都等于\(a_n\),那么就是将\(a_2...a_{n-1}\)加了\(m\),那平均数就是\(((\sum_{i=2}^{n-1}a_i)+m)\),如果不是,那么就是先将每个数补到\(m\),需要用\(t\)次,那就剩下\(m-t\)没操作,现在和已经是\(a_n*(n-2)\)了,所以在\(a_n\)的基础上\(+(m-t-1)/(n-2+1)+(m-t-1)/%(n-2+1)\)将剩下的\(m-t\)次操作平均分配到\(a_2...a_n\)里,记得有余数。
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int n,m;
int mmax=0,mmin=1<<30;
int a[100010],sum;
signed main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(n);read(m);
for (int i=1;i<=n;i++) read(a[i]),mmin=min(mmin,a[i]),mmax=max(mmax,a[i]),sum+=a[i];
sum-=mmax+mmin;
if (mmax*(n-2)-sum>=m) printf("%lld",(sum+m));
else m-=mmax*(n-2)-sum,sum=mmax*(n-2)+(m-1)/(n-1)*(n-2)+(m-1)%(n-1),printf("%lld",sum);
// fclose(stdin);fclose(stdout);
return 0;
}
「EZEC-10」排列排序(题目)
题目大意:给出\(a_1...a_n\),每次可以将一个区间\([l,r]\)排序,花费\(r-l+1\)的代价,问使得\(a\)有序的最少代价
分析:
1.我们发现如果要让\(a_i\)回到\(a_{a_i}\)的位置,那么至少要花费\(|a_i-a_{a_i}+1|\)的代价,那么其实就是每次将\(min(a_i,a_{a_i})\)~\(max(a_i,a_{a_i})+1\),之后的需要操作的区间总长就是答案了(即\(>0\)的位置)
2. 区间\(+1\)操作就要差分来维护,你要线段树也可以,YBH线段树
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t;
int n,a[1000100],b[1000100],c[1000100];
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
read(n);
for (int i=1;i<=n;i++)
read(a[i]),b[i]=0;
for (int i=1;i<=n;i++)
if (i<a[i])
b[i]+=1,b[a[i]+1]-=1;
int ans=0;
for (int i=1;i<=n;i++)
c[i]=c[i-1]+b[i];
for (int i=1;i<=n;i++)
if (c[i]>0) ans++;
printf("%d\n",ans);
}
// fclose(stdin);fclose(stdout);
return 0;
}
「EZEC-10」Shape(题目)
题目大意:如图所示
分析:
- 为了方便,我将白色点设为\(1\),黑色点设为\(0\)
- 在设一个数组\(lr[i][j]\)表示第\(i\)行第\(j\)个想左边最多有多少个连续的白色(包括自己),在设一个数组\(ud[i][j]\)表示第\(i\)行第\(j\)个可以向上下同时延申多少格,这两个数组都可以用前缀和\(O(n*m)\)维护
- 通过\(lr[i][j]\)我们可以知道每条横着的连续白点的位置,在通过\(ud[i][j]\)我们就可以得到这样一幅图这就是这些白点的一个连通块,将它们排序按\(up\),并不会影响它们之间组成\(H\)形的数量,对于\(up[i][j]\)的来说他能与每一个\(up[k][l]\)大于它的构成\(up[i][j]\)个,因为与顺序无关,所以只用算比他大的个数乘以\(up[i][j]\)就可以了,时间复杂度是\(O(n*m*\log_2m)\)
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
long long ans=0;
int n,m;
int mid_up_max[2*1010][2*1010],mid_down_max[2*1010][2*1010];
int up_down_min[2*1010][2*1010];
int left_right_max[2*1010][2*1010],up_down_min_sum[2*1010][2*1010];
int a[2*1010][2*1010];
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(n),read(m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
read(a[i][j]),a[i][j]=!a[i][j];
for (int i=1;i<=n;i++)
{
left_right_max[i][m+1]=-1;
for (int j=m;j>=1;j--)
if (a[i][j]==1)
left_right_max[i][j]=left_right_max[i][j+1]+1;
else
left_right_max[i][j]=-1;
}
for (int j=1;j<=m;j++)
{
mid_up_max[0][j]=-1;
for (int i=1;i<=n;i++)
if (a[i][j]==1)
mid_up_max[i][j]=mid_up_max[i-1][j]+1;
else
mid_up_max[i][j]=-1;
}
for (int j=1;j<=m;j++)
{
mid_down_max[n+1][j]=-1;
for (int i=n;i>=1;i--)
if (a[i][j]==1)
mid_down_max[i][j]=mid_down_max[i+1][j]+1;
else
mid_down_max[i][j]=-1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
up_down_min[i][j]=min(mid_up_max[i][j],mid_down_max[i][j]);
/*
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
printf("%d ",left_right_max[i][j]);
printf("\n");
}
printf("\n");
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
printf("%d ",up_down_min[i][j]);
printf("\n");
}
printf("\n");
*/
/*
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (up_down_min[i][j]==-1)
up_down_min_sum[i][j]=-1;
else if (up_down_min[i][j-1]==-1)
up_down_min_sum[i][j]=up_down_min[i][j];
else up_down_min_sum[i][j]=up_down_min_sum[i][j-1]+up_down_min[i][j];
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
printf("%d ",up_down_min_sum[i][j]);
printf("\n");
}
printf("\n");
*/
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (a[i][j]==1)
{
int s[2*1010],len=0;
for (int k=j;k<=j+left_right_max[i][j];k++)
s[++len]=up_down_min[i][k];
sort(s+1,s+len+1);
for (int k=1;k<=len;k++)
ans+=(len-k)*s[k];
j=j+left_right_max[i][j];
}
printf("%lld\n",ans);
// fclose(stdin);fclose(stdout);
return 0;
}
「EZEC-10」Covering(题目)
题目大意:有\(k\)张长度为\(1*2\)的纸片,从中选出\(l\)~\(r\)个,按从编号小到编号大的顺序放入\(n*m\)的方格中,后放入的会覆盖前面的,使得最后的方格如给出的\(n*m\)的方格一样,问有几种放法
分析:
- 分三步走,令\(ans=1\)
- 先将编号在棋盘中出现两次的方格去掉,它们不会影响答案
- 找出出现过一次的纸片,对于每一张,它都会有上下左右编号大于它的纸片的总和的放法,\(ans*上下左右大于它的总和数\)
- 对于没有出现过的,我们设\(f[i][j]\)表示\(1\)~\(i\)张纸条中,选出\(j\)个的放置方案总数,则我们要求的就是\(f[没有出现过的总数][max(0,l-出现过的总数)]+...+f[没有出现过的总数][r-出现过的总数]\),最后将它乘上\(ans\)就好了,这个东西可以用\(DP\)来求,转移式\(f[i][j]=i的放法*f[i-1][j]+f[i][j-1]\)
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
#define mod 1000000007
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t;
int n,m,k,l,r;
struct point { int cnt,x[2],y[2]; }p[1010];
int v[1010][1010],vl,a[1010][1010];
int f[1010][1010];
int num[1010],ans,len,bis;
int fx[4]={1,0,-1,0};
int fy[4]={0,1,0,-1};
int calc(int x,int y)
{
int g=0;
for (int t=0;t<=3;t++)
{
int tx=x+fx[t],ty=y+fy[t];
if (a[tx][ty]>a[x][y]) g++;
}
return g;
}
void down(int x,int y)
{
for (int t=0;t<=3;t++)
{
int tx=x+fx[t],ty=y+fy[t];
vl+=v[tx][ty];vl%=mod;
}
v[x][y]=1;
}
int max(int x,int y) { if (x>y) return x; else return y; }
signed main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
memset(f,0,sizeof(f));
memset(num,0,sizeof(num));ans=len=0;bis=1;
memset(p,0,sizeof(p));memset(v,0,sizeof(v));
memset(a,0,sizeof(a));
read(n),read(m),read(k);read(l),read(r);vl=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
read(a[i][j]);
if (a[i][j]!=0)
p[a[i][j]].x[p[a[i][j]].cnt]=i,p[a[i][j]].y[p[a[i][j]].cnt]=j,p[a[i][j]].cnt++;
}
for (int i=k;i>=1;i--)
{
if (p[i].cnt==0) num[++len]=vl%mod;
else if (p[i].cnt==1) bis=bis*calc(p[i].x[0],p[i].y[0])%mod,down(p[i].x[0],p[i].y[0]);
else down(p[i].x[0],p[i].y[0]),down(p[i].x[1],p[i].y[1]);
}
f[len][0]=1;
for (int i=1;i<=len;i++) f[i][1]=(f[i-1][1]+num[i])%mod;
for (int i=2;i<=len;i++)
for (int j=2;j<=i;j++)
f[i][j]=((f[i-1][j-1]*num[i]%mod)+f[i-1][j])%mod;
// printf("%lld %lld %lld\n",l,k,len);
for (int i=max(0,l-k+len);i<=r-k+len;i++)
ans=(f[len][i]+ans)%mod;
// printf("%lld %lld\n",ans,bis);
printf("%lld\n",bis*ans%mod);
}
// fclose(stdin);fclose(stdout);
return 0;
}
「EZEC-10」序列(题目)
题目大意:如题
分析:
- 根据给出的关系式,将每个有关联的连一条边,边权为\(z_i\)
2, 这样我们就得到了许多个联通块,每个连通块之间互补干扰,我们单独讨论一个连通块- 我们可以从一个点出发,遍历到每一个点,根据异或的结合律,我们就可以用起点表示出连通块中的每一个点,如果一个点有不同的表达方式,则无解
- 这样我们就要找到一个值,令起点为这个值,使得连通块上的每一点都小于\(k\),其实问的就是一个异或的最大值,容易想到用\(trie\)树来维护
- 假设这个连通块的点为\(a_1,a_2..a_n\),令\(a_2=a_1 \otimes g_1,...a_n=a_1 \otimes g_n\),将\(g_1..g_n\)插入树中,然后从根节点开始遍历,\(x,d,sum\)表示当前节点的编号,\(d\)表示当前在第几位,\(sum\)表示当前的异或值
- ①如果\(x\)没有儿子,证明已经匹配完了,如果此时的\(sum \leqslant k\),说明是可行的返回\(1\),否则返回\(0\)
②如果有两个儿子,那无论这一位是什么,都可以异或到\(1\),所以\(sum+2^d\),\(dfs01\)儿子
③如果有一个儿子,我们设它为\(0\)儿子,(\(1\)儿子同理)如果\(sum+2^d\leqslant k\)说明这一位取 \(0\) 时,异或值为\(0\),肯定都是可以的,可以返回的答案 \(+2^d\),再将\(sum+2^d\),\(dfs\)\(1\)儿子- 最后所有返回值相乘就是答案
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mod 1000000007
#define int long long
using namespace std;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
struct trie
{
int ch[31*5*100010][2];
int tot;
void init()
{
for (int i=1;i<=tot;i++) ch[i][0]=ch[i][1]=0;
tot=1;
}
void add(int x)
{
int now=1;
for (int i=30;i>=0;i--)
{
int t=(x>>i)&1;//printf("%d %d\n",now,t);
if (ch[now][t]==0)
{
tot++;
ch[now][t]=tot;
}
now=ch[now][t];
}
}
}tr;
int n,m,k,ans=1;
struct node { int i,j,x; }p[5*100010];
struct edge { int x,y,next,c; }a[5*2*100010];
int len,last[5*100010];
int bz[5*100010],re,vis[5*100010];
void ins(int x,int y,int c)
{
len++,a[len].x=x,a[len].y=y,a[len].c=c;
a[len].next=last[x],last[x]=len;
}
void wwww(int x,int fa)
{
if (re==1) return ;
tr.add(bz[x]);
vis[x]=1;
for (int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if (y!=fa)
{
if (bz[y]!=-1 && (bz[x]^a[k].c)!=bz[y]) { re=1; return ; }
bz[y]=bz[x]^a[k].c;
if (vis[y]==-1) wwww(y,x);
}
}
}
int dfs(int x,int d,int sum)
{
if (sum>k) return 0;
if (tr.ch[x][0]==0 && tr.ch[x][1]==0) return sum<=k;
if (tr.ch[x][0]>0 && tr.ch[x][1]>0)
{
sum+=(1<<d);
return dfs(tr.ch[x][0],d-1,sum)+dfs(tr.ch[x][1],d-1,sum);
}
if (tr.ch[x][0]>0)
{
if (sum+(1<<d)<=k) return (1<<d)+dfs(tr.ch[x][0],d-1,sum+(1<<d));
else return dfs(tr.ch[x][0],d-1,sum);
}
if (tr.ch[x][1]>0)
{
if (sum+(1<<d)<=k) return (1<<d)+dfs(tr.ch[x][1],d-1,sum+(1<<d));
else return dfs(tr.ch[x][1],d-1,sum);
}
}
signed main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(n),read(m),read(k);
for (int i=1;i<=n;i++) bz[i]=-1,vis[i]=-1;
for (int i=1;i<=m;i++)
{
read(p[i].i),read(p[i].j),read(p[i].x);
ins(p[i].i,p[i].j,p[i].x),ins(p[i].j,p[i].i,p[i].x);
}
for (int i=1;i<=n;i++)
{
if (bz[i]==-1)
{
if (last[i]==0) ans=ans*(k+1)%mod;
else
{
tr.init();
re=0;
bz[i]=0,wwww(i,-1);
if (re==1) { printf("0"); return 0; }
ans=ans*dfs(1,30,0)%mod;
}
}
}
printf("%lld\n",ans);
// fclose(stdin);fclose(stdout);
return 0;
}
浙公网安备 33010602011771号