校内模拟赛(20170913)
救命啊,救命啊!学长出丧题啦!!!
学长他们压榨我们的劳动力,然后带着我们的成绩跑了,无奈的我们只好玩命的调程序,把学长留给我们的丧题做完(划掉)
65分的rank 1跑路。
————————————————我是分割线————————————————
T1:粉饰(decorate)
【题目描述】
小D有一块被分为n*m个格子的矩形鱼片。为了装饰鱼片,小D决定给每个格子上色。由于小D很喜欢红白,所以小D给每个格子涂上了红色或白色,第i行第j列的格子颜色记为c[i,j]。涂完之后,小D想评估这块鱼片的“XY值”。我们定义一个有序无重复三元格子组{(x1,y1),(x2,y2),(x3,y3)}为“XY组”当且仅当:
|(x1-x2)*(y1-y2)|+|(x3-x2)*(y3-y2)|=0
(c[x1,y1]-c[x2,y2])*(c[x3,y3]-c[x2,y2])≠0
一块鱼片的“XY值”为该块鱼片里“XY组”的数量。
【输入数据】
第一行两个正整数n,m。
为描述整块鱼片,接下来n行,每行一个长度为m的01串,0表示白色,1表示红色。
【输出数据】
输出一行,一个整数表示这块鱼片的“XY值”。
【样例输入】
3 3
011
100
011
【样例输出】
44
【数据范围】
本题采用子任务制。
Subtask 1(20pts):1<=n,m<=100;
Subtask 2(10pts):n=1;
Subtask 3(20pts):n=3;
Subtask 4~5(各25pts) 没有数据范围限制;
对于100%的数据,1<=n*m<=4*10^6,0<=c[i][j]<=1。
【样例解释】
由于本题比较特殊,所以没有样例解释。
————————————————我是分割线————————————————
T1:sb题
显然我们就只有两种情况:1、3个点都在同一排,2、三个点呈L形,然后我们前缀和统计每行每列0和1总和,枚举中间点,预处理组合数C(n,2),直接暴力出答案就好了。
但是!学长!他出了爆long long的数据啊!又是爆long long这种坑点。。上次已经被学长坑过一次了。。(Cry~~~~~)
那么对于这种情况的处理方法,我们用两个long long压答案就好了,因为每次加上的数都不会超过long long....
#include<cstdio> #define MN 4000005 using namespace std; int h[MN][2],r[MN][2],a[MN]; long long qaq[MN]; int n,m; long long ans1,ans2; void add(long long q){ans1+=q,ans2=ans1>=1e18?ans2+1:ans2,ans1=ans1>=1e18?ans1-1e18:ans1;} int main(){ freopen("decorate.in","r",stdin); freopen("decorate.out","w",stdout); scanf("%d%d",&n,&m); int x;char ch; for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ ch=getchar(); while(ch!='0'&&ch!='1')ch=getchar(); a[(i-1)*m+j]=ch-'0'; } qaq[1]=qaq[0]=0;qaq[2]=1; for(int i=3;i<=4000000;i++)qaq[i]=qaq[i-1]+i-1; for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)x=a[(i-1)*m+j],h[i][x]++,r[j][x]++; for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)add((qaq[h[i][a[(i-1)*m+j]^1]]+qaq[r[j][a[(i-1)*m+j]^1]]+h[i][a[(i-1)*m+j]^1]*r[j][a[(i-1)*m+j]^1])*2); if(ans2)printf("%lld%018lld\n",ans2,ans1); else printf("%lld\n",ans1); fclose(stdin); fclose(stdout); }
————————————————我是分割线————————————————
T2:洞悉(insight)
【题目描述】
在走出了第6扇门后,小I终于可以使用他之前获得的水晶球了。当他透过水晶球看向前方,发现门的后面,是一扇又一扇无尽的门。n个房间排在一起,笔直地延伸向远方。为了让自己接下来的体验不算太差,小I想知道这n个房间中其中一些房间的信息,并进行一些修改。每个房间都有一个seed值。而小I有两种操作:
1 x y:询问[x,y]区间的房间的seed值的乘积对1000000007的模;
2 l r:将[l,r]区间里所有房间的seed值改为φ(seed)。
其中,φ(x)为欧拉函数,即小等于x的与x互质的数的个数。
【输入数据】
第一行两个正整数n,m。
第二行n个正整数,表示每个房间的seed值。
接下来m行,每行表示一个小I的操作。
【输出数据】
对于每个操作1,输出一行询问的答案。
【样例输入】
5 6
1 2 4 8 9
1 1 2
2 2 4
2 1 3
1 1 5
1 2 3
2 3 5
【样例输出】
32
4
24
【数据范围】
对于20%的数据,n,m<=1000;
对于40%的数据,n,m<=50000;
另外20%的数据,seed<=100000。
对于100%的数据,1<=n,m<=200000,1<=seed<=10^7。
【样例解释】
下面给出每次操作得到的结果:
①序列变为1 1 4 8 9
②1*4*8=32
③1*1*4=4
④序列变为1 1 2 4 6
⑤序列变为1 1 1 4 6
⑥1*4*6=24
————————————————我是分割线————————————————
这道题目明显的线段树,但是有一个问题,那就是我们做不到区间修改,因为一个区间的乘积取完欧拉函数值后和区间内的数每一个取欧拉函数值后的乘积没有任何关系。
所以我们只能单点修改,很快我们发现一个性质,那就是我们一个点最多只能修改24次(至于为什么可以自己拿个暴力跑一下),所以我们记一下区间的修改次数,如果>24就不修改了。
相当简单的QAQ,但是我忘记加#include<cstdio>导致编译错误了(这种错误绝不能再犯了。。。这是我的锅,怒丢了100分)
下面贴代码:
#include<iostream> #include<cstdio> #include<string> #define MN 200005 #include<cstring> using namespace std; const int MAXN=10000005; const int mod=1000000007; bool vis[MAXN]; int phi[MAXN],a[MN],prime[MAXN],n,m,opt,x,y; struct tr{ long long num; int tt; }t[MN<<2]; void Prime(int n) { int cnt=0; memset(vis,0,sizeof(vis)); for(int i=2;i<n;i++) { if(!vis[i]) { prime[cnt++]=i; phi[i]=i-1; } for(int j=0;j<cnt&&i*prime[j]<n;j++) { __int64 k=i*prime[j]; vis[k]=1; if(i%prime[j]==0) { phi[k]=phi[i]*prime[j]; break; } else phi[k]=phi[i]*(prime[j]-1); } } } void build(int l,int r,int k){ if(l==r){t[k].num=a[l];return;} int mid=l+r>>1; build(l,mid,k<<1); build(mid+1,r,k<<1|1); t[k].num=t[k<<1].num*t[k<<1|1].num%mod; } void update(int l,int r,int a,int b,int k){ if(a<=l&&r<=b){ t[k].tt++; if(l==r){ t[k].num=phi[t[k].num]; return; } } int mid=l+r>>1; if(a<=mid&&t[k].tt<21)update(l,mid,a,b,k<<1); if(b>mid&&t[k].tt<21)update(mid+1,r,a,b,k<<1|1); t[k].num=t[k<<1].num*t[k<<1|1].num%mod; } long long query(int l,int r,int a,int b,int k){ if(l==a&&r==b)return t[k].num; int mid=l+r>>1; if(b<=mid)return query(l,mid,a,b,k<<1); if(a>mid) return query(mid+1,r,a,b,k<<1|1); return (query(l,mid,a,mid,k<<1)*query(mid+1,r,mid+1,b,k<<1|1))%mod; } int main() { freopen("insight.in","r",stdin); freopen("insight.out","w",stdout); Prime(10000001);phi[1]=1; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); build(1,n,1); while(m--){ scanf("%d%d%d",&opt,&x,&y); if(opt==1)update(1,n,x,y,1); else printf("%lld\n",query(1,n,x,y,1)); } fclose(stdin); fclose(stdout); }
————————————————我是分割线————————————————
命令(order)
【题目描述】
小O开了许多年飞机,现在她准备更换自己的炮台。于是就有很多炮台来应聘。为了选拔最优秀的炮台,小O给炮台们下了一条指令,要求他们在n个数中,选出若干个数,使得它们两两之间的和不为质数,最后使得这些数的乘积尽可能大。作为一名优秀的炮台,为了使自己处于尴尬的境地,你需要又快又好地解决这个问题。
【输入数据】
第一行一个正整数n。
第二行n个正整数a1~an,表示小O给出的数字。
【输出数据】
输出一行表示最大乘积,答案对10^9+7取模。
【样例输入】
6
3 2 2 3 4 4
【样例输出】
64
【数据范围】
本题采用子任务制。
Subtask 1(10pts):n<=13;
Subtask 2(12pts):n<=23;
Subtask 3(13pts):ai<=20;
Subtask 4(15pts):ai<=2000;
Subtask 5~6(各25pts):没有数据范围限制;
对于100%的数据,1<=n<=1000,1<=ai<=5*10^5。
【样例解释】
选取2、2、4、4四个数,2*2*4*4=64。
————————————————我是分割线————————————————
本次考试最丧的一题。
首先我们考虑一个性质:对于除2以外的数,素数都是奇数。
又由于我们取1是没有必要的(除非只有一个1,但是这也不影响答案,下面会讲);
所以我们根据上面的性质,我们把数分为2部分,奇数和偶数。(保证两边自己内部不会产生奇数)
然后我们对于两个相加是素数的点,相互连inf的边;跑一个最小割就好了
但是由于我们这题是乘积最大!所以我们要化乘为加,方法是对每条边取log
还有个问题,我们不能直接根据答案得出方案。所以我们还需要。。。根据网络流的特性判断方案。
从S开始bfs,对于左半边的点,如果我们发现可以遍历到,就表示需要纳入答案,对于右半边的点,如果我们发现不能遍历到,那么就需要纳入答案(至于为什么,考虑一下最小割的性质,在这里我就不讲了。)
然后就解决啦!!手都断了。。
考后自己打了一边,竟然一遍过,感觉自己萌萌哒~,看来集训还是有效的
下面贴代码:
#include<cstdio> #include<cstring> #include<cmath> #define min(a,b) ((a)<(b)?(a):(b)) #define eps 1e-8 #define mod 1000000007 #define MN 1005 using namespace std; double aabs(double aa){return aa>0?aa:-aa;} int head[MN],iter[MN],level[MN],que[MN],a[MN],b[2][MN],n,tt1,tt2,num=1,S,T; struct edge { int to,next; double w; }g[MN*MN<<1]; bool pri[MN*MN],u[MN],vis[MN]; void bfs(){ int h=1,t=1;vis[S]=u[S]=1;que[1]=S; while(h<=t){ int tmp=que[h++]; for(int i=head[tmp];i;i=g[i].next)if(!vis[g[i].to]&&g[i].w>eps)u[g[i].to]=vis[g[i].to]=1,que[++t]=g[i].to; } } bool spfa(){ memset(level,0,sizeof(level)); memcpy(iter,head,sizeof(head)); int h=1,t=1;level[S]=1;que[1]=S; while(h<=t){ int tmp=que[h++]; for(int i=head[tmp];i;i=g[i].next)if(!level[g[i].to]&&g[i].w>eps)level[g[i].to]=level[tmp]+1,que[++t]=g[i].to; }return level[T]!=0; } double dfs(int u,double flow){ if(u==T)return flow; double used=0; for(int &i=iter[u];i;i=g[i].next) if(level[g[i].to]==level[u]+1&&g[i].w>eps){ double qaq=dfs(g[i].to,min(g[i].w,flow-used)); g[i].w-=qaq,g[i^1].w+=qaq,used+=qaq; if(aabs(used-flow)<eps)return flow; } return used; } void dinic(){ double now; while(spfa())do now=dfs(S,1e9);while(now>eps); } void ins(int u,int v,double w){g[++num].next=head[u];head[u]=num;g[num].to=v;g[num].w=w;} void insw(int u,int v,double w){ins(u,v,w);ins(v,u,0);} int main(){ freopen("order.in","r",stdin); freopen("order.out","w",stdout); for(int i=2;i<=1000000;i++)for(int j=i+i;j<=1000000;j+=i)pri[j]=true;pri[1]=true; scanf("%d",&n);S=0,T=n+1; for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]%2][a[i]%2?++tt2:++tt1]=a[i]; for(int i=1;i<=tt1;i++)for(int j=1;j<=tt2;j++)if(!pri[b[0][i]+b[1][j]])insw(i,j+tt1,1e9); for(int i=1;i<=tt1;i++)insw(S,i,log2(b[0][i])); for(int i=1;i<=tt2;i++)insw(i+tt1,T,log2(b[1][i])); dinic(),bfs(); long long ans=0; for(int i=1;i<=tt1;i++)if(u[i])ans=ans==0?b[0][i]:(ans*b[0][i])%mod; for(int i=1;i<=tt2;i++)if(!u[i+tt1])ans=ans==0?b[1][i]:(ans*b[1][i])%mod; printf("%lld\n",ans); fclose(stdin); fclose(stdout); }