[总结] NOIP 前的考试记录
sb博主又犯sb错误了!
他觉得以往模拟赛因为犯sb错误扔的分足足有1k分了!
于是他想记录一下自己犯的sb错误看看自己到底有多sb!
嗯就从今天开始吧
2018.9.28
1. 二分边界写错。骚什么啊卡那么紧的上界是要干啥啊开1e9是能死啊?
2. 如果差分完要求前缀和的话记得扫两遍啊扫两遍,只扫一遍求的是原数组的值记住了
3. 真心立flag-->空间再开炸就剁手!!
4. 一个数 x 想求它遍历模 n 剩余系的话应该是 (n-1)/gcd(x,n),不是(n-1)/x 注意了
5. 啊双模数哈希的时候一定一定要特别小心敲错变量名,比如说把mod2敲成mod1,base1敲成base2之类的。。。也许可以考虑起名的时候起两个相差很远的名字?
6. 我他妈!再打错文件!他妈女装!
7.千万!千万!别用三目运算符!能用if就不要用三目运算符!90->50!
8. 忽略上面那条 md自己点分治打错了还怪三目运算符 三目运算符:这锅我不背
以下原文:
写在前面
由于我天天被虐,模拟考还老是考的极差,感觉有必要开个博客记录一下每场考试
由于懒得加美元符所以这篇博客不用 latex
由于要弄代码折叠我还特意换掉了 Markdown 编辑器
由于我没话说了,前言就这么多吧
2018.7.13
开场看 T1 发现不会顿时贼虚,思路明明马上就是正解了但是死活没想出来。把点权放到边上求出来最大生成树之后立马敲了个树剖(我只是生来码农) 然后跟暴力一样 n^2 求的两点边权最小值
看 T2 也是发现性质可以开两个树状数组做但是死活没想出来答案从哪找,结果发现莫队一脸可做码了个带修莫队还把块大小设成根号 n 了。(我果然菜到连前一天刚讲过的板子都不会敲了
结果 T2 被卡但是还是比暴力多 20 分
T3 更不可做 直接30分爆搜走人
总分 50+50+30=130 本部 rank3/16
T1 这东西应该能发现可以从大到小加边合并集合啊这不跟走廊泼水节一样么
#include<cstdio> #include<cctype> #include<algorithm> #define N 100005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int val[N]; int father[N]; int d[N],fa[N]; int n,m,cnt,tot; int cme[N],fs[N]; int sze[N],son[N]; int dfn[N],top[N]; int head[N],mn[N<<2]; struct Edge{ int to,nxt,dis; }edge[N<<1]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } struct Node{ int x,y,dis; friend bool operator<(Node a,Node b){ return a.dis>b.dis; } }node[N*10]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } int find(int x){ if(father[x]==x) return x; return father[x]=find(father[x]); } void file(){ freopen("zoo.in","r",stdin); freopen("zoo.out","w",stdout); } signed main(){ file(); n=getint(),m=getint(); for(int i=1;i<=n;i++){ father[i]=i; sze[i]=1; val[i]=getint(); } for(int i=1;i<=m;i++){ node[i].x=getint(); node[i].y=getint(); node[i].dis=min(val[node[i].x],val[node[i].y]); } int now=0; std::sort(node+1,node+1+m); for(int i=1;i<=m;i++){ int x=find(node[i].x); int y=find(node[i].y); if(x==y) continue; father[x]=y; now+=sze[x]*sze[y]*node[i].dis; sze[y]+=sze[x]; } printf("%lld\n",now<<1); return 0; }
T2 可以开两个树状数组分别存每个点之前线段左、右端点各出现了多少次。
然后查询就是用右端点之前出现了多少次减去左端点减一之前出现了多少次
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 200005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int tot,cnt; int g[N<<1]; int l[N],r[N]; int T,n,cas,m; int ques[N][5]; int xgl[N],xgr[N]; struct BIT{ int f[N<<1]; void clear(){ memset(f,0,sizeof f); } int query(int x){ int ans=0; for(;x>=1;x-=x&-x) ans+=f[x]; return ans; } void add(int x,int y){ for(;x<=m;x+=x&-x) f[x]+=y; } }a,b; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("segment.in","r",stdin); freopen("segment.out","w",stdout); T=getint(); while(T--){ a.clear();b.clear(); cnt=tot=0; n=getint(); for(int i=1;i<=n;i++){ ques[i][1]=getint(); ques[i][2]=getint(); if(ques[i][1]){ xgl[i]=l[ques[i][2]]; xgr[i]=r[ques[i][2]]; } else{ cnt++; ques[i][3]=ques[i][2]+cnt; l[cnt]=ques[i][2]; r[cnt]=ques[i][3]; g[++tot]=ques[i][2]; g[++tot]=ques[i][3]; } } std::sort(g+1,g+1+tot); m=std::unique(g+1,g+1+tot)-g-1; printf("Case #%d:\n",++cas); for(int i=1;i<=n;i++){ if(ques[i][1]==0){ ques[i][2]=std::lower_bound(g+1,g+1+m,ques[i][2])-g; ques[i][3]=std::lower_bound(g+1,g+1+m,ques[i][3])-g; printf("%d\n",b.query(ques[i][3])-a.query(ques[i][2]-1)); a.add(ques[i][2],1); b.add(ques[i][3],1); } else{ xgl[i]=std::lower_bound(g+1,g+1+m,xgl[i])-g; xgr[i]=std::lower_bound(g+1,g+1+m,xgr[i])-g; a.add(xgl[i],-1); b.add(xgr[i],-1); } } } return 0; }
T3 什么鬼啊喂 2^((n-1)*(m-1)-k) 是啥啊 noip 压轴题靠打表的啊啊啊?
证明:不会(×)
#include<cstdio> #include<cctype> #include<cstring> #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int k; int n,m,p; int ksm(int a,int b){ int ans=1; if(b<0) return ans; while(b){ if(b&1) ans=ans*a%p; a=a*a%p; b>>=1; } return ans; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("number.in","r",stdin); freopen("number.out","w",stdout); n=getint();m=getint();k=getint(); for(int i=1;i<=k;i++) int a=getint(),b=getint(),c=getint(); p=getint(); if((n+m)%2==1) return printf("0"),0; printf("%lld\n",ksm(2,(n-1)*(m-1)-k)); return 0; }
2018.7.15
辣鸡选手T1写挂T2写萎还不看T3
考试过程大概就是写了50分钟T1感觉有100分了然后去写T2树形背包查错三小时无果交30分暴力结果暴力写挫变成10分T1也只有20
rank...没敢看 大约全场倒数前十吧?
T1 考试思路是对的就是用扩欧求出特解然后求通解。但是实际上不用像考场上那么复杂分16种情况。。考虑扩欧最后得出的解 x,y,让x取最小正整数解,那么y就相应取到了最大正整数解(其实这样说不是很准确,实际上是“有限制”的最大正整数解),然后大概可以判断如果此时a*b<0的时候显然有无穷多解。否则再让y对 a/gcd(a,b) 取模得出y的最小正整数解,然后作差再除以 a/gcd(a,b) 就是y能取到的正整数解数量。至于为什么这时x也是正数,试着证一下:因为此时a,b都是同号了。假设ab同正,那么y变小的同时x是加上一个正数,不断变大,恒正。假设ab同负,那么y变小是加上一个负数,相应的x应该减去一个负数,还是恒正。证毕。所以x是满足要求的,只需要考虑y就行了。
代码懒得写了要考虑太多情况。。
T2 好题 考场上状态定义有误导致这东西的dp有后效性然而最后20分钟才查出来当时正慌的一批想不出来正解了。以下正解:
定义f[i][j]表示以i为根子树中选j个黑点所有的边对答案的最大贡献 其实这种考虑贡献的题见了好多回了可每次就是想不到。有了dp方程就很好转移了,枚举x的当前子树中选j个,之前的子树中选k个,式子就能通过枚举当前子树中的黑点乘上外边的黑点加上当前子树的白点乘上外边的白点再乘上边权转移了
#include<cstdio> #include<cctype> #define N 2005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m,cnt; int sze[N],g[N][N]; int head[N],f[N][N]; struct Edge{ int to,nxt,dis; }edge[N<<1]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void dp(int now){ sze[now]=1; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(sze[to]) continue; dp(to); sze[now]+=sze[to]; for(int j=0;j<=m;j++) g[now][j]=f[now][j]; for(int j=0;j<=sze[to] and j<=m;j++){ for(int k=0;k<=sze[now]-sze[to] and k<=m;k++){ if(j+k>m) break; f[now][j+k]=max(f[now][j+k],f[to][j]+g[now][k]+j*(m-j)*edge[i].dis+(sze[to]-j)*(n-sze[to]-m+j)*edge[i].dis); } } } } signed main(){ n=getint(),m=getint(); for(int i=1;i<n;i++){ int a=getint(),b=getint(),c=getint(); add(a,b,c);add(b,a,c); } dp(1); printf("%lld\n",f[1][m]); return 0; }
T3 什么切比雪夫距离不会(×) 然而我发现网上一大半讲切比雪夫距离的博客都拿边长为1的正方形举例子然而那实际上应该是根号二略略略
2018.7.19
开场发现三道题全被早晨zyz押中了“lyd不就考dp图论么”%%%
T1 感觉水水dp但是死活想不出来咋做,后来有了一个n三方的做法枚举前i个选j组上一个是k,然后发现了一个奇怪的性质选j组可以不枚举然后就n方水过了这题
#include<cstdio> #include<cctype> #define N 5005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n; int dp[N][3],val[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void file(){ freopen("tower.in","r",stdin); freopen("tower.out","w",stdout); } signed main(){ file(); n=getint(); for(int i=1;i<=n;i++) val[i]=val[i-1]+getint(); for(int i=1;i<=n;i++){ for(int j=0;j<i;j++){ if(val[i]-val[j]>=val[j]-val[dp[j][0]]){ if(dp[j][1]+1>dp[i][1]){ dp[i][1]=dp[j][1]+1; dp[i][0]=j; } else if(dp[j][1]+1==dp[i][1]){ dp[i][0]=max(dp[i][0],j); } } } } printf("%lld\n",n-dp[n][1]); return 0; }
T2 看完一分钟就有思路十分钟敲完代码结果没考虑这天啥也不干的情况直接干成0分我大概是个傻逼好学生天天光想着学习从不摸鱼
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define M 105 #define N 10005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int t,n,m; int ycl[M]; int dp[N][M]; struct W{ int c,d; friend bool operator<(W a,W b){ return a.c<b.c; } }w[N]; struct C{ int m,l,a; friend bool operator<(C x,C y){ return x.m<y.m; } }c[M]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } signed main(){ freopen("wrk.in","r",stdin); freopen("wrk.out","w",stdout); t=getint(),m=getint(),n=getint(); for(int i=1;i<=m;i++){ c[i].m=getint(); c[i].l=getint(); c[i].a=getint(); } for(int i=1;i<=n;i++){ w[i].c=getint(); w[i].d=getint(); } std::sort(c+1,c+1+m); std::sort(w+1,w+1+n); memset(ycl,0x3f,sizeof ycl); for(int i=1;i<=n;i++) ycl[w[i].c]=min(ycl[w[i].c],w[i].d); for(int i=1;i<=100;i++) ycl[i]=min(ycl[i],ycl[i-1]); int now=1; memset(dp,0xcf,sizeof dp); dp[0][1]=0; for(int i=0;i<=t;i++){ for(int j=1;j<=100;j++){ dp[i+ycl[j]][j]=max(dp[i+ycl[j]][j],dp[i][j]+1); while(now<=m and c[now].m==i){ dp[i+c[now].l][c[now].a]=max(dp[i+c[now].l][c[now].a],dp[i][j]); now++; } } } int ans=0; for(int i=1;i<=100;i++) ans=max(ans,dp[t][i]); printf("%d\n",ans); return 0; }
T3 显然二分枚举选哪个点能包含中间那个根然后用链长公式什么的搞一下就行
#include<queue> #include<cstdio> #include<cctype> #include<cstring> #define N 2005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int tot; int deg[N]; int vis[N]; int d[N],ro; int head[N]; int n,m,k,cnt; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void dfs(int now){ vis[now]=1;tot++; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(vis[to]) continue; dfs(to); } } bool check(int x){ for(int i=1;i<=n;i++){ memset(d,0,sizeof d); memset(vis,0,sizeof vis); std::queue<int> q; q.push(i); vis[i]=1; while(q.size()){ int u=q.front();q.pop(); if(d[u]==x) continue; for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(vis[to]) continue; vis[to]=1; d[to]=d[u]+1; q.push(to); } } if(!vis[ro]) continue; int now=1; for(int j=1;j<=n;j++){ if(vis[j]) continue; tot=0; dfs(j); now+=(tot-1)/(x*2+1)+1; } if(now<=k){ //printf("x=%d,i=%d,now=%d\n",x,i,now); return 1; } } return 0; } signed main(){ freopen("holes.in","r",stdin); freopen("holes.out","w",stdout); n=getint(),m=getint(),k=getint(); if(k>=n) return printf("0\n"),0; int maxn=0,minn=1e9; for(int i=1;i<=m;i++){ int a=getint(),b=getint(); add(a,b);add(b,a); deg[a]++,deg[b]++; int p=max(deg[a],deg[b]); maxn=max(maxn,p); } for(int i=1;i<=n;i++) minn=min(minn,deg[i]); if(maxn<=2){ int l=1,r=n,ans=0; while(l<=r){ int mid=l+r>>1; if((n-1)/(mid*2+1)+1<=k) ans=mid,r=mid-1; else l=mid+1; } return printf("%d\n",ans),0; } for(int i=1;i<=n;i++){ if(deg[i]>2) ro=i; } int l=1,r=n,ans=0; while(l<=r){ int mid=l+r>>1; if(check(mid)) r=mid-1,ans=mid; else l=mid+1; } printf("%d\n",ans); return 0; }
总分 100+0+100=200 本部rank3 md明明能AK的
2018.7.20
水题欢乐赛
拿到题发现都知道做法,只是T3的扫描线从没写过,心里问自己,我是不是要AK了(flag)
先看T1,是个线段树维护最大子段和,还是单点修改,这不随便做,噼里啪啦敲完顺便过了拍
再看T2,裸矩乘,没啥意义的题,全场切 (orz GXZ legend)
此时离考试结束还有两个小时零二十分钟,开始刚T3
原来没写过扫描线,但是还是听说过怎么做的,貌似是按横坐标排序然后纵坐标离散化线段树维护当前纵坐标覆盖的长度?
yy了一阵感觉很靠谱,妈妈我会扫描线了!
然后码码码,码完测一下样例。咦,怎么wa了? 哦原来这里要加个标记。不怂,调!
码码码,码完测一下样例。咦,怎么又wa了?哦这个标记不能这么用。调!
终于过了样例,随手出了组数据,成功hack掉自己的程序。原来是读优没判负数
就这样调调调,突然意识到一个事情,线段树上维护的应该是区间不应该是点,那有点麻烦啊,但是还是靠着自己强大的yy能力yy出了一个新算法
码完过了样例和手造数据
敲完暴力开始对拍,结果一拍一wa
此时离考试结束还有三十分钟。
意识有点模糊了,手也有点发抖了,赶紧把自己写的暴力加上文件扔进了考试文件夹。想着要是调不出来我就认了。
还有十五分钟。
突然发现有个地方没有pushdown,不动声色的加上了pushdown,Ctrl+B编译,测样例,过了,测手造数据,过了。对拍,拍上了。(安排上了)
长舒一口气,瘫在了椅子上。再次证明了自己强无敌的yy能力。
考试结束发现T3莫名RE一个点 (flag)
发现数据出锅了 造数据的来你来告诉我一条线段它tm的算不算矩形
然而最后也没改成绩
总分100+100+90=290 本部rank6 mmp
T1 线段树支持单点修改维护最大子段和
#include<cstdio> #include<cctype> #define N 500005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m; int val[N],sum[N<<2]; int mx[N<<2],lmx[N<<2],rmx[N<<2]; struct Node{ int sum,mx,lmx,rmx; }; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void pushup(int cur){ sum[cur]=sum[cur<<1]+sum[cur<<1|1]; lmx[cur]=max(lmx[cur<<1],sum[cur<<1]+lmx[cur<<1|1]); rmx[cur]=max(rmx[cur<<1|1],sum[cur<<1|1]+rmx[cur<<1]); mx[cur]=max(sum[cur],max(lmx[cur<<1|1]+rmx[cur<<1],max(mx[cur<<1],mx[cur<<1|1]))); } void build(int cur,int l,int r){ if(l==r){ sum[cur]=mx[cur]=lmx[cur]=rmx[cur]=val[l]; return; } int mid=l+r>>1; build(cur<<1,l,mid); build(cur<<1|1,mid+1,r); pushup(cur); } Node query(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return (Node){sum[cur],mx[cur],lmx[cur],rmx[cur]}; int mid=l+r>>1; if(ql>mid) return query(cur<<1|1,mid+1,r,ql,qr); if(qr<=mid) return query(cur<<1,l,mid,ql,qr); Node a=query(cur<<1,l,mid,ql,qr); Node b=query(cur<<1|1,mid+1,r,ql,qr); Node c;c.sum=c.mx=c.lmx=c.rmx=0; c.sum=a.sum+b.sum; c.lmx=max(a.lmx,a.sum+b.lmx); c.rmx=max(b.rmx,b.sum+a.rmx); c.mx=max(c.sum,max(a.rmx+b.lmx,max(a.mx,b.mx))); return c; } void modify(int cur,int l,int r,int ql,int qr,int c){ if(ql<=l and r<=qr){ sum[cur]=lmx[cur]=rmx[cur]=mx[cur]=c; return; } int mid=l+r>>1; if(ql<=mid) modify(cur<<1,l,mid,ql,qr,c); else modify(cur<<1|1,mid+1,r,ql,qr,c); pushup(cur); } signed main(){ freopen("BRS.in","r",stdin); freopen("BRS.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=n;i++) val[i]=getint(); build(1,1,n); while(m--){ if(getint()==1){ int x=getint(),y=getint(); printf("%d\n",query(1,1,n,x,y).mx); } else{ int x=getint(),y=getint(); modify(1,1,n,x,x,y); } } return 0; }
T2 裸矩乘
#include<cstdio> #include<cctype> #include<cstring> #define N 12 #define int long long const int mod=7777777; #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m; int x[N]; struct Mat{ int a[N][N]; void clear(){ memset(a,0,sizeof a); } void init(){ clear(); for(int i=1;i<=m;i++) a[i][i]=1; } friend Mat operator*(Mat x,Mat y){ Mat z;z.clear(); for(int i=1;i<=m;i++){ for(int k=1;k<=m;k++){ for(int j=1;j<=m;j++) z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod; } } return z; } }cs,f; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } Mat ksm(Mat x,int y){ Mat ans;ans.init(); while(y){ if(y&1) ans=ans*x; x=x*x; y>>=1; } return ans; } signed main(){ freopen("fyfy.in","r",stdin); freopen("fyfy.out","w",stdout); m=getint(),n=getint(); cs.clear();f.clear(); f.a[1][1]=1; int sum=1; for(int i=2;i<=m;i++){ f.a[1][i]=sum; sum+=f.a[1][i]; } if(n<=m-1){ printf("%lld\n",f.a[1][n+1]); return 0; } for(int i=1;i<m;i++) cs.a[i+1][i]=1; for(int i=1;i<=m;i++) cs.a[i][m]=1; f.a[1][1]=1; cs=ksm(cs,n-m+1); f=f*cs; printf("%lld\n",f.a[1][m]); return 0; }
T3 扫描线,说起来还是第一个完全靠自己学会的算法
考场代码:
#include<cstdio> #include<cctype> #include<algorithm> #define N 505 #define int long long void pushdown(int,int,int); #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int g[N]; int n,tot,pos; int now[N<<2],d[N<<2]; int sum[N<<2],flag[N<<2]; int lazy[N<<2],real[N<<2]; struct Node{ int x,y1,y2; int type; friend bool operator<(Node a,Node b){ return a.x<b.x; } }node[N<<1]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void build(int cur,int l,int r){ real[cur]=g[r]-g[l]; if(r-l==1){ d[cur]=1; return; } int mid=l+r>>1; build(cur<<1,l,mid); build(cur<<1|1,mid,r); d[cur]=d[cur<<1]+1; } void pushup(int cur,int l,int r){ //sum[cur]=sum[cur<<1]+sum[cur<<1|1]; now[cur]=now[cur<<1]+now[cur<<1|1]; flag[cur]=min(flag[cur<<1],flag[cur<<1|1]); //printf("cur=%lld,l=%lld,r=%lld,d=%lld\n",cur,l,r,d[cur]); /*if(d[cur]!=2){ now[cur]=now[cur<<1]+now[cur<<1|1]; //printf("now1=%lld,now2=%lld,now=%lld\n",now[cur<<1],now[cur<<1|1],now[cur]); return; } //printf("flag1=%lld,flag2=%lld\n",flag[cur<<1],flag[cur<<1|1]); now[cur]=0; int mid=l+r>>1; if(flag[cur<<1] and flag[cur<<1|1]) now[cur]=real[cur]; else if(flag[cur<<1]) now[cur]=g[mid]-g[l]; else if(flag[cur<<1|1]) now[cur]=g[r]-g[mid]; else now[cur]=0;*/ } void dfs(int cur,int l,int r){ //printf("cur=%lld,l=%lld,r=%lld\n",cur,l,r); if(r-l==1){ if(flag[cur]) now[cur]=real[cur]; else now[cur]=0; return; } pushdown(cur,l,r); int mid=l+r>>1; dfs(cur<<1,l,mid); dfs(cur<<1|1,mid,r); pushup(cur,l,r); } void pushdown(int cur,int l,int r){ if(!lazy[cur]) return; int mid=l+r>>1;/* sum[cur<<1]+=(mid-l)*lazy[cur]; sum[cur<<1|1]+=(r-mid)*lazy[cur]; if(sum[cur<<1]>=mid-l) flag[cur<<1]=1,now[cur<<1]=real[cur<<1]; if(sum[cur<<1|1]>=r-mid) flag[cur<<1|1]=1,now[cur<<1|1]=real[cur<<1|1]; //if(sum[cur<<1]<mid-l) dfs(cur<<1,l,mid); //if(sum[cur<<1|1]<r-mid) dfs(cur<<1|1,mid,r);*/ flag[cur<<1]+=lazy[cur]; flag[cur<<1|1]+=lazy[cur]; lazy[cur<<1]+=lazy[cur]; lazy[cur<<1|1]+=lazy[cur]; lazy[cur]=0; } void modify(int cur,int l,int r,int ql,int qr,int c){ //printf("in::cur=%lld,l=%lld,r=%lld,ql=%lld,qr=%lld,now=%lld\n",cur,l,r,ql,qr,now[cur]); if(ql<=l and r<=qr){ if(c==1){ //sum[cur]+=r-l; lazy[cur]++; flag[cur]++; dfs(cur,l,r); //now[cur]=real[cur]; } else{ //sum[cur]-=r-l; flag[cur]--; lazy[cur]--; //if(sum[cur]<r-l) dfs(cur,l,r); } return; } int mid=l+r>>1; pushdown(cur,l,r); if(ql==mid){ modify(cur<<1|1,mid,r,ql,qr,c); goto s; } if(qr==mid){ modify(cur<<1,l,mid,ql,qr,c); goto s; } if(ql<mid) modify(cur<<1,l,mid,ql,qr,c); if(mid<qr) modify(cur<<1|1,mid,r,ql,qr,c); s:; pushup(cur,l,r); //printf("out::cur=%lld,l=%lld,r=%lld,ql=%lld,qr=%lld,now=%lld\n",cur,l,r,ql,qr,now[cur]); } int query(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return now[cur]; } signed main(){ freopen("olddriver.in","r",stdin); freopen("olddriver.out","w",stdout); n=getint(); for(int i=1;i<=n;i++){ int a=getint(),b=getint(),c=getint(),d=getint(); node[++tot].x=a;node[tot].y1=d;node[tot].y2=b;node[tot].type=1; node[++tot].x=c;node[tot].y1=d;node[tot].y2=b;node[tot].type=2; g[++pos]=b;g[++pos]=d; } std::sort(node+1,node+1+tot); std::sort(g+1,g+1+pos); int m=std::unique(g+1,g+1+pos)-g-1; for(int i=1;i<=tot;i++){ node[i].y1=std::lower_bound(g+1,g+1+m,node[i].y1)-g; node[i].y2=std::lower_bound(g+1,g+1+m,node[i].y2)-g; } build(1,1,m); int ans=0; for(int i=1;i<=tot;i++){ ans+=query(1,1,m,1,m)*(node[i].x-node[i-1].x); //printf("i=%lld,x=%lld,y2=%lld,y1=%lld\n",i,node[i].x,node[i].y2,node[i].y1); //printf("query=%lld,ans=%lld\n",query(1,1,m,1,m),ans); modify(1,1,m,node[i].y2,node[i].y1,node[i].type); } printf("%lld\n",ans); return 0; }
2018.7.23
话说上次写的时候没刹住车直接飙成游记了啊。。。
为了节省时间以后就考场上的考试过程就少写点啊
拿到题看T1,一眼性质题,楞推了二十分钟大概有个O(nm)的做法,本机测一发极限数据发现要4.3s,感觉要凉,再一看时间限制6s,瞬间稳得不行。去看T2,发现是牛客比赛原题,当时zyz打了这个比赛还跟我炫耀做出来这题来着。。。知道了结论直接敲了个n三方上去。此时刚过了五十分钟,开始刚T3。过程略。
考试结束,发现自己得的所有分是考试开始前一个小时得的mmp。
总分100+100+0=200,本部 rank2
T1 性质题,前缀和优化一下就行了
#include<ctime> #include<cstdio> #include<cctype> #define int long long const int N=2005; #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m; int a[N][N],b[N][N]; int qzha[N][N],qzhb[N][N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void file(){ freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); } signed main(){ file(); n=getint(),m=getint(); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ a[i][j]=getint(); qzha[i][j]=qzha[i-1][j]+a[i][j]; } } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ b[i][j]=getint(); qzhb[i][j]=qzhb[i][j-1]+b[i][j]; } } while(m--){ int c=getint(),d=getint(),x=getint(),y=getint(),ans=0; int cc=min(c,x),xx=max(c,x),dd=min(d,y),yy=max(d,y); for(int i=1;i<=n;i++) ans+=(qzha[xx][i]-qzha[cc-1][i])*(qzhb[i][yy]-qzhb[i][dd-1]); printf("%lld\n",ans); } return 0; }
T2 性质题,集合点只可能在所有的横坐标和纵坐标中。
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 55 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) inline int abs(int x){return x<0?-x:x;} int n; int dis[N]; int ans[N]; int x[N],y[N]; int lenx,leny; int nx[N],ny[N]; int xx[N*N],yy[N*N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void file(){ freopen("tower.in","r",stdin); freopen("tower.out","w",stdout); } signed main(){ file(); n=getint(); for(int i=1;i<=n;i++){ x[i]=getint(),y[i]=getint(); nx[i]=x[i];ny[i]=y[i]; } std::sort(nx+1,nx+1+n); std::sort(ny+1,ny+1+n); lenx=std::unique(nx+1,nx+1+n)-nx-1; leny=std::unique(ny+1,ny+1+n)-ny-1; memset(ans,0x3f,sizeof ans); for(int i=1;i<=lenx;i++){ for(int j=1;j<=leny;j++){ for(int p=1;p<=n;p++) dis[p]=abs(nx[i]-x[p])+abs(ny[j]-y[p]); std::sort(dis+1,dis+1+n); int sum=0; for(int p=1;p<=n;p++){ sum+=dis[p]; ans[p]=min(ans[p],sum); } } } for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }
T3 最大匹配的话直接网络流二分图什么的都能求,但是方案就不行了,又因为给出的是一棵树,我们考虑树形DP。
定义 f[i][0/1],g[i][0/1]分别表示以点i为根的子树中,选了/不选点i的最大匹配数与其方案。如果不选i的话,就需要在它的所有儿子里求和,即 $\sum \max(f[to][0],f[to][1])$ 咦不是不用Latex么
别的也是一样更新就好
考场上的主要障碍是一直局限在树形DP的老套路里,就是D完一个儿子之后立马更新答案,其实可以把所有儿子都DP完之后再统一更新。再写个高精啥的就行了。
#include<cstdio> #include<cctype> #include<cstring> #define N 1005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int stk[N],top; int n,cnt,d[N]; int f[N][5],tot; int qzh[N],hzh[N]; int head[N],sze[N]; struct BigInteger{ int a[205]; void clear(){ memset(a,0,sizeof a); } friend BigInteger operator+(BigInteger x,BigInteger y){ BigInteger z; z.clear(); int jw=0; for(int i=1;i<=100;i++){ z.a[i]=x.a[i]+y.a[i]+jw; jw=z.a[i]/10; z.a[i]%=10; } return z; } friend BigInteger operator*(BigInteger x,BigInteger y){ BigInteger z; z.clear(); for(int i=1;i<=100;i++){ int jw=0; for(int j=1;j<=100;j++){ z.a[i+j-1]+=x.a[i]*y.a[j]+jw; jw=z.a[i+j-1]/10; z.a[i+j-1]%=10; } z.a[i+100]=jw; } return z; } void print(){ int flag=0; for(int i=100;i;i--){ if(flag or a[i]) printf("%d",a[i]),flag=1; } if(!flag) printf("0"); } }g[N][5]; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void dfs(int now){ sze[now]=1; for(int i=head[now];i;i=edge[i].nxt) dfs(edge[i].to),sze[now]+=sze[edge[i].to]; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(max(f[to][0],f[to][1])==0) continue; f[now][0]+=max(f[to][0],f[to][1]); if(f[to][0]==f[to][1]) g[now][0]=g[now][0]*(g[to][0]+g[to][1]); else if(f[to][0]>f[to][1]) g[now][0]=g[now][0]*g[to][0]; else g[now][0]=g[now][0]*g[to][1]; } for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; int nnow=f[to][0]+1; BigInteger ans=g[to][0]; for(int j=head[now];j;j=edge[j].nxt){ int too=edge[j].to; if(to==too) continue; if(max(f[too][0],f[too][1])==0) continue; nnow+=max(f[too][0],f[too][1]); if(f[too][0]==f[too][1]) ans=ans*(g[too][0]+g[too][1]); else if(f[too][0]>f[too][1]) ans=ans*g[too][0]; else ans=ans*g[too][1]; } if(nnow>f[now][1]) f[now][1]=nnow,g[now][1]=ans; else if(nnow==f[now][1]) g[now][1]=g[now][1]+ans; } } signed main(){ n=getint(); for(int i=1;i<=n;i++){ int x=getint(),y=getint(); g[x][0].clear();g[x][1].clear(); g[x][0].a[1]=1; g[x][1].a[1]=1; while(y--){ int z=getint(); add(x,z); d[z]++; } } int root; for(int i=1;i<=n;i++){ if(!d[i]) root=i; } dfs(root); printf("%lld\n",max(f[root][0],f[root][1])); BigInteger ans;ans.clear(); if(f[root][0]==f[root][1]) ans=g[root][0]+g[root][1]; else if(f[root][0]>f[root][1]) ans=g[root][0]; else ans=g[root][1]; ans.print(); return 0; }
2018.8.6
开场看T1发现是道输入极其恶心的模拟题,眼残把题目中保证没有两个水管在同一高度看成了不保证。。于是题目变得十分复杂起来,硬推了一个小时把如何将这种很复杂的情况考虑进去想清楚了,然后敲+调了一个小时。花了两个小时做了一道半个小时就能写完的题。然后看T2发现是一道区间DP,但是数组开不下,光是状态数就有1e9。想尽各种办法优化都无能为力,突然看到部分分是跟m有关,于是想到离散化,那么就可做了。然后调调调最后一分钟才调出来也是紧张+刺激。
但是因为姿势原因TLE了一个点,实际上不用离散化的。 T3略,只是稳拿20分变成10分有点伤心。
总分 100+100+10=210 本部rank1,全场rank8
T1 直接放考场代码了,支持更加复杂的情况
#include<cstdio> #include<cctype> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define N 1005 #define M 120000 int a[M]; int n,m,tot; void dfs(int); int zbx[M][3]; // 1->up 2->down int zby[M][3]; // 1->left 2->right char ch[N][N]; int cme[M],dwn[M]; int sze[M],str[M]; int qzh1[M],qzh2[M]; struct Node{ int son,h; }b[M]; std::vector<Node> v[M]; bool cmp(Node x,Node y){ return x.h>y.h; } bool cmp2(Node x,Node y){ return x.son<y.son; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } int getnum(int x,int y){ int ans=0; for(int i=y;i;i++){ if(!isdigit(ch[x][i])) return ans; ans=ans*10+ch[x][i]-'0'; } } void file(){ freopen("clickbait.in","r",stdin); freopen("clickbait.out","w",stdout); } //dfs2->find the index of one's son //fx: 1->left 2->right 3->down int dfs2(int x,int y,int fx){ // printf("x=%d,y=%d,fx=%d\n",x,y,fx); if(ch[x][y]=='+'){ if(fx==1 or fx==2) return dfs2(x+1,y,3); else{ if(ch[x][y+1]=='-') return dfs2(x,y+1,2); else return dfs2(x,y-1,1); } } int a,b,c,d; if(fx==3 and ch[x+1][y]=='-'){ for(int i=y;i;i--){ if(ch[x+1][i]=='+'){ a=i; break; } } for(int i=y;i;i++){ if(ch[x+1][i]=='+'){ b=i; break; } } for(int i=x+2;i;i++){ if(ch[i][a]=='+'){ c=i; break; } } int num=0; for(int i=x+1;i<=c;i++){ for(int j=a;j<=b;j++){ if(isdigit(ch[i][j])){ num=getnum(i,j); break; } } if(num) break; } zbx[num][1]=x+1;zbx[num][2]=c; zby[num][1]=a;zby[num][2]=b; tot++; dfs(num); return num; } if(fx==1) return dfs2(x,y-1,fx); if(fx==2) return dfs2(x,y+1,fx); if(fx==3) return dfs2(x+1,y,fx); } void dfs(int now){ sze[now]=(zbx[now][2]-zbx[now][1])*(zby[now][2]-zby[now][1]); for(int i=zbx[now][1];i<=zbx[now][2];i++){ if(ch[i][zby[now][1]-1]=='-'){ int to=dfs2(i,zby[now][1]-1,1); v[now].push_back((Node){to,i}); str[now]+=str[to]+zbx[to][1]-i; sze[now]+=sze[to]; } } for(int i=zbx[now][1];i<=zbx[now][2];i++){ if(ch[i][zby[now][2]+1]=='-'){ int to=dfs2(i,zby[now][2]+1,2); v[now].push_back((Node){to,i}); str[now]+=str[to]+zbx[to][1]-i; sze[now]+=sze[to]; } } std::sort(v[now].begin(),v[now].end(),cmp); qzh1[0]=qzh2[0]=0; for(int i=0;i<v[now].size();i++){ int to=v[now][i].son; //printf("to=%d,sze=%d,str=%d\n",to,sze[to],str[to]); if(i==0) qzh1[0]=sze[to],qzh2[0]=str[to]; else{ if(v[now][i].h==v[now][i-1].h){ a[to]+=qzh1[i-2]+qzh2[i-2]; qzh1[i]=qzh1[i-1]+sze[to]; qzh2[i]=qzh2[i-1]+str[to]; } else{ a[to]+=qzh1[i-1]+qzh2[i-1]; qzh1[i]=qzh1[i-1]+sze[to]; qzh2[i]=qzh2[i-1]+str[to]; } } b[to].son=sze[to]+str[to]; b[to].h=to; } } void print(int x){ printf("x=%d\n",x); printf("zbx1=%d,zbx2=%d,zby1=%d,zby2=%d\n",zbx[x][1],zbx[x][2],zby[x][1],zby[x][2]); } void dfs3(int now){ for(int i=0;i<v[now].size();i++){ int to=v[now][i].son; a[to]+=a[now]; b[to].son+=a[to]; dfs3(to); } } signed main(){ file(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",ch[i]+1); int starx=0,stary=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(ch[i][j]=='+'){ starx=i; stary=j; break; } } if(starx) break; } zbx[1][1]=starx;zby[1][1]=stary; for(int i=stary+1;i<=m;i++){ if(ch[starx][i]=='+'){ zby[1][2]=i; break; } } for(int i=starx+1;i<=n;i++){ if(ch[i][stary]=='+'){ zbx[1][2]=i; break; } } tot=1; dfs(1); dfs3(1); std::sort(b+1,b+1+tot,cmp2); //for(int i=1;i<=tot;i++) // printf("i=%d,a=%d,b=%d\n",i,a[i],b[i].son); for(int i=2;i<=tot;i++) printf("%d\n",b[i].h); printf("1"); return 0; }
T2 放这个垃圾版本的区间DP,实际上不用离散化,把精灵当做状态而不是把房子当做状态更加优秀。
%:pragma GCC optimize(2) #include<cstdio> #include<cctype> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define M 105 #define re register #define in inline int g[M],tot; int fz[M][M]; int qzh[M][M]; int vis[M][4005]; int n,m,k,maxn,len; int f[2005][M][M][2]; struct Node{ int a,b,t; friend bool operator<(Node x,Node y){ return x.t<y.t; } }node[M]; std::vector<Node> v[M]; in int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } in void file(){ freopen("go.in","r",stdin); freopen("go.out","w",stdout); } in int bsearch(int d,int l,int r,int x){ int ans=r; while(l<=r){ int mid=l+r>>1; if(fz[d][mid]>=x) ans=mid-1,r=mid-1; else l=mid+1; } //printf("l=%d,r=%d,d=%d,x=%d,ans=%d\n",l,r,d,x,ans); return ans; } in int calc(int x,int y){ if(~vis[x][y]) return vis[x][y]; if(!fz[x][0]) return vis[x][y]=0; int it=bsearch(x,1,fz[x][0],y); return vis[x][y]=qzh[x][fz[x][0]]-qzh[x][it]; } signed main(){ file(); n=getint(),k=getint(),m=getint(); for(re int i=1;i<=m;i++){ node[i].a=getint(),node[i].b=getint(),node[i].t=getint(); g[++tot]=node[i].a; maxn=std::max(maxn,node[i].t); } g[++tot]=k; std::sort(g+1,g+1+tot); len=std::unique(g+1,g+1+tot)-g-1; k=std::lower_bound(g+1,g+1+tot,k)-g; for(re int i=1;i<=m;i++){ node[i].a=std::lower_bound(g+1,g+1+len,node[i].a)-g; v[node[i].a].push_back((Node){node[i].a,node[i].b,node[i].t}); fz[node[i].a][++fz[node[i].a][0]]=node[i].t; } for(re int i=1;i<=len;i++) std::sort(fz[i]+1,fz[i]+1+fz[i][0]),std::sort(v[i].begin(),v[i].end()); for(re int i=1;i<=len;i++){ if(!fz[i][0]) continue; for(re int j=1;j<=fz[i][0];j++) qzh[i][j]=qzh[i][j-1]+v[i][j-1].b; } memset(vis,-1,sizeof vis); memset(f,0xcf,sizeof f); int ans=0; f[1][k][k][0]=calc(k,1); for(re int i=1;i<=maxn;i++){ for(re int j=1;j<=len;j++){ for(re int p=j;p<=len;p++){ //f[i][j][p][0/1]->f[i+1][j-1][p][0] or f[i+1][j][p+1][1] f[i+1][j][p][0]=std::max(f[i+1][j][p][0],f[i][j][p][0]); f[i+1][j][p][1]=std::max(f[i+1][j][p][1],f[i][j][p][1]); ans=std::max(ans,std::max(f[i+1][j][p][0],f[i+1][j][p][1])); for(int at=0;at<=1;at++){ int now=at?p:j; if(j-1>=1){ if(i+g[now]-g[j-1]<=maxn) f[i+g[now]-g[j-1]][j-1][p][0]=std::max(f[i+g[now]-g[j-1]][j-1][p][0],f[i][j][p][at]+calc(j-1,i+g[now]-g[j-1])); } if(p+1<=len){ if(i+g[p+1]-g[now]<=maxn) f[i+g[p+1]-g[now]][j][p+1][1]=std::max(f[i+g[p+1]-g[now]][j][p+1][1],f[i][j][p][at]+calc(p+1,i+g[p+1]-g[now])); } } } } } printf("%d\n",ans); return 0; }
然后是B组题解。
T1 观察到有用的点只有k+2个,于是在求完这些点两两之间最短路之后,状压一下就行了。注意k=0的情况要特判一下。
#include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 50005 #define int long long int is[12]; int f[12][1<<12]; int dis[N],vis[N]; int n,m,k,s,t,cnt; int head[N],d[15][15]; struct Edge{ int to,nxt,dis; }edge[N<<1]; struct Node{ int now,dis; friend bool operator<(Node x,Node y){ return x.dis>y.dis; } }; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } bool flag=0; void dijkstra(int fr,int id){ memset(vis,0,sizeof vis); for(int i=1;i<=n;i++) dis[i]=2e18;dis[fr]=0; std::priority_queue<Node> pq;pq.push((Node){fr,0}); while(pq.size()){ int u=pq.top().now;pq.pop(); if(vis[u]) continue; vis[u]=1; for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(dis[to]>dis[u]+edge[i].dis){ dis[to]=dis[u]+edge[i].dis; pq.push((Node){to,dis[to]}); } } } for(int i=1;i<=k;i++) d[id][i]=dis[is[i]]; d[id][k+2]=dis[t]; if(fr==s and dis[t]>=2e18) printf("-1"),flag=1; return; } signed main(){ //freopen("in.txt","r",stdin); n=getint(),m=getint(),k=getint(),s=getint(),t=getint(); for(int i=1;i<=m;i++){ int x=getint(),y=getint(),z=getint(); add(x,y,z); } for(int i=1;i<=k;i++) is[i]=getint(); for(int i=1;i<=k;i++) dijkstra(is[i],i); dijkstra(s,k+1); if(flag) return 0; if(!k){ printf("%lld\n",dis[t]); return 0; } int maxn=1<<k; for(int i=1;i<=k;i++){ for(int j=1;j<maxn;j++) f[i][j]=2e18; } for(int i=1;i<=k;i++) f[i][1<<i-1]=d[k+1][i]; for(int i=1;i<maxn;i++){ for(int j=1;j<=k;j++){ if(i&(1<<j-1)){ for(int p=1;p<=k;p++){ if(!(i&(1<<p-1))) f[p][i|(1<<p-1)]=std::min(f[p][i|(1<<p-1)],f[j][i]+d[j][p]); } } } } int ans=2e18; for(int i=1;i<=k;i++) f[i][maxn-1]+=d[i][k+2]; for(int i=1;i<=k;i++) ans=std::min(ans,f[i][maxn-1]); printf("%lld\n",ans); return 0; }
T2 对于C操作,贪心的都先选上,然后放进一个小根堆里目的是为了支持撤销,如果遇到E操作就从小根堆里撤销即可。
#include<queue> #include<cstdio> #include<cctype> #include<cstring> #define N 200005 int n,ans; std::priority_queue<int,std::vector<int>,std::greater<int> > pq; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ n=getint(); for(int i=1;i<=n;i++){ char ch[10];scanf("%s",ch); int x=getint(); if(ch[0]=='c'){ ans+=x;pq.push(x); } else if(i!=n){ while(pq.size()>=x){ int t=pq.top();pq.pop(); ans-=t; } } else{ if(pq.size()>=x) printf("%d\n",ans); else printf("-1"); } } return 0; }
T3 先用组合数求出三角形的情况,然后再减去三点共线的所有情况就好。如何求三点共线的情况呢?对于每个点,求出其它点与它的斜率,对于有同一斜率的m个点,那么关于这个点重复计算的个数就是C(m,2)。
但是这样会减重,只需要内层循环从1到i即可。
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 3005 #define db double #define ll long long ll ans; db fz[N]; db x[N],y[N]; db h[N],z[N]; int n,tot,aa,bb; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void work(db *a,int m){ db last=-1e9;int now=0; for(int i=1;i<=m;i++){ if(a[i]==last) now++; else{ if(now<2){ last=a[i]; now=1; } else{ ans-=(ll)now*(now-1)/2; last=a[i]; now=1; } } } if(now>=2) ans-=(ll)now*(now-1)/2; } signed main(){ freopen("triangle.in","r",stdin); freopen("triangle.out","w",stdout); n=getint(); for(int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]); ans=(ll)n*(n-1)*(n-2)/6; for(int i=1;i<=n;i++){ tot=0; aa=0; bb=0; for(int j=1;j<i;j++){ if(x[i]!=x[j]){ if(y[i]==y[j]) h[++aa]=y[i]; else{ db a=(y[i]-y[j])/(x[i]-x[j]); fz[++tot]=a; } } else z[++bb]=x[i]; } std::sort(h+1,h+1+aa); std::sort(z+1,z+1+bb); std::sort(fz+1,fz+1+tot); work(h,aa);work(z,bb);work(fz,tot); } printf("%lld\n",ans); return 0; }
2018.8.8
开场看T1T2发现都不会,感觉T3是最可做的,就去刚T3。想了一个前缀和+并查集的方法,结果发现这题丧心病狂的卡空间。突然没了思路,于是转过头去看T1,发现是道博弈,但是没想到DP的做法,于是写了个记搜。然后看T2,是道巨恶心的模拟+爆搜,不想做,于是去敲T3的暴力。想了个分块+并查集+二分答案的做法,复杂度1e8,很稳,敲完调完还剩四十分钟。急忙转过头去写T2。本来以为T1和T3能拿很多分的,想着T2调不出来我也就认了,没想到写完之后看一会就找到了一个错,再看一会又找到了一个错。没多久就过了所有样例。
出成绩 80+50+20=150,本部rank2,全场有点惨,rank16.
T1 这玩意可以DP做,然后后缀和优化一下就行了
#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 5005 using std::max; using std::min; int n,m,k; int ans[N]; int val[N<<1]; int dp[2][N]; int qzh[2][N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("vode.in","r",stdin);freopen("vode.out","w",stdout); n=getint(),m=getint(),k=getint(); for(int i=1;i<=n;i++) val[i]=getint(); for(int i=n+1;i<=n+m;i++) val[i]=val[i-n]; int cur=0; for(int i=n+m;i;i--){ for(int j=m-1;j;j--){ int r=min(j+k,m-1); if(val[i]==val[i+1]){ if(qzh[cur^1][j+1]-qzh[cur^1][r+1]>0) dp[cur][j]=1; else dp[cur][j]=0; } else{ if(qzh[cur^1][j+1]-qzh[cur^1][r+1]>0) dp[cur][j]=0; else dp[cur][j]=1; } qzh[cur][j]=qzh[cur][j+1]+dp[cur][j]; } cur^=1; memset(qzh[cur],0,sizeof qzh[cur]); if(i<=n){ for(int j=1;j<=k;j++) ans[i]|=dp[cur^1][j]; } } for(int i=1;i<=n;i++){ printf("%d ",ans[i]?val[i]:val[i]^1); } return 0; }
T2 正解是最短路,正常走路操作向四个方向连边权为1的边,向四个方向最近的墙连边权为到最近的墙的距离的边,然后跑最短路即可。
#include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 505 using std::min; using std::max; int n,m,s,t; int dis[N*N]; int vis[N*N]; char ch[N][N]; int head[N*N],cnt; int upx[5][N][N],upy[5][N][N]; int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; inline int abs(int x){return x<0?-x:x;} inline int idx(int x,int y){return (x-1)*n+y;} struct Edge{ int to,nxt,dis; }edge[N*N*10]; struct Node{ int now,val; friend bool operator<(Node x,Node y){ return x.val>y.val; } }; std::priority_queue<Node> pq; void dijkstra(){ pq.push((Node){s,0});dis[s]=0; while(pq.size()){ int u=pq.top().now;pq.pop(); if(vis[u]) continue; for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(dis[to]>dis[u]+edge[i].dis){ dis[to]=dis[u]+edge[i].dis; pq.push((Node){to,dis[to]}); } } } } void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("portal.in","r",stdin);freopen("portal.out","w",stdout); n=getint(),m=getint(); memset(dis,0x3f,sizeof dis); for(int i=1;i<=n;i++){ scanf("%s",ch[i]+1); for(int j=1;j<=m;j++){ if(ch[i][j]=='C') s=idx(i,j); if(ch[i][j]=='F') t=idx(i,j); if(i-1>0 and ch[i-1][j]!='#') upx[0][i][j]=upx[0][i-1][j],upy[0][i][j]=upy[0][i-1][j]; else upx[0][i][j]=i-1,upy[0][i][j]=j; if(j-1>0 and ch[i][j-1]!='#') upx[1][i][j]=upx[1][i][j-1],upy[1][i][j]=upy[1][i][j-1]; else upx[1][i][j]=i,upy[1][i][j]=j-1; } } for(int i=n;i;i--){ for(int j=m;j;j--){ if(i+1<=n and ch[i+1][j]!='#') upx[2][i][j]=upx[2][i+1][j],upy[2][i][j]=upy[2][i+1][j]; else upx[2][i][j]=i+1,upy[2][i][j]=j; if(j+1<=m and ch[i][j+1]!='#') upx[3][i][j]=upx[3][i][j+1],upy[3][i][j]=upy[3][i][j+1]; else upx[3][i][j]=i,upy[3][i][j]=j+1; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(ch[i][j]=='#') continue; for(int k=0;k<4;k++){ int nx=i+dx[k]; int ny=j+dy[k]; if(nx>0 and nx<=n and ny>0 and ny<=m and ch[nx][ny]!='#') add(idx(i,j),idx(nx,ny),1); } int ans=1e9; for(int k=0;k<4;k++) ans=min(ans,abs(upx[k][i][j]-i)+abs(upy[k][i][j]-j)); for(int k=0;k<4;k++){ int aaa; if(k==0 or k==2) aaa=2-k; if(k==1 or k==3) aaa=4-k; int nx=upx[k][i][j]+dx[aaa]; int ny=upy[k][i][j]+dy[aaa]; add(idx(i,j),idx(nx,ny),ans); } } } dijkstra(); printf(dis[t]>=0x3f3f3f3f?"nemoguce":"%d\n",dis[t]); return 0; }
T3 按秩合并的并查集,观察到一个性质就是按秩合并的并查集树高是 $O(\log n)$ 级别的,所以可以建出一棵带权并查集树,边权为这两个集合相连的最早时间。对于每个询问,找出它们路径上的最大值即可。
#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 100005 using std::max; using std::swap; int n,m,q,dis[N],h[N]; int head[N],cnt,f[N][22]; int father[N],d[N],mx[N][22]; struct Edge{ int to,nxt,dis; }edge[N]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int find(int x){ if(father[x]==x) return x; return find(father[x]); } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void dfs(int now){ for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; f[to][0]=now; d[to]=d[now]+1; mx[to][0]=edge[i].dis; for(int i=1;i<=20;i++){ f[to][i]=f[ f[to][i-1] ][i-1]; mx[to][i]=max(mx[to][i-1],mx[f[to][i-1]][i-1]); } dfs(to); } } int query(int x,int y){ if(d[x]<d[y]) swap(x,y); int ans=0; for(int i=20;~i;i--){ if(d[f[x][i]]>=d[y]) ans=max(ans,mx[x][i]),x=f[x][i]; } if(x==y) return ans; for(int i=20;~i;i--){ if(f[x][i]!=f[y][i]){ ans=max(ans,max(mx[x][i],mx[y][i])); x=f[x][i],y=f[y][i]; } } return max(ans,max(mx[x][0],mx[y][0])); } signed main(){ freopen("pictionary.in","r",stdin); freopen("pictionary.out","w",stdout); n=getint(),m=getint(),q=getint(); for(int i=1;i<=n;i++) father[i]=i,h[i]=1; for(int i=1;i<=m;i++){ int x=m-i+1; for(int j=2;j;j++){ if(j*x>n) break; int r1=find(x); int r2=find(j*x); if(r1==r2) continue; if(h[r1]<h[r2]) swap(r1,r2); h[r1]=max(h[r1],h[r2]+1); father[r2]=r1; add(r1,r2,i); } } int x=find(1); dfs(x); while(q--){ int x=getint(),y=getint(); printf("%d\n",query(x,y)); } return 0; }
B组
T1 一眼最大生成树,然后再跑一个最小生成树就行了。
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 50005 #define db double db xx[N]; int fr[N]; int n,m,s,t; int father[N]; struct Edge{ int x,y; db dis; friend bool operator<(Edge a,Edge b){ return a.dis<b.dis; } }node[100005]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } int find(int x){ if(father[x]==x) return x; return father[x]=find(father[x]); } signed main(){ freopen("trip.in","r",stdin);freopen("trip.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=m;i++){ node[i].x=getint(); node[i].y=getint(); scanf("%lf",&node[i].dis); } int tot=0; for(int i=1;i<=n;i++){ int p=getint();tot+=p; while(p--) fr[getint()]=i; } for(int i=1;i<=n;i++) scanf("%lf",&xx[i]); for(int i=1;i<=m;i++){ if(fr[node[i].x]==fr[node[i].y]) node[i].dis=node[i].dis*xx[fr[node[i].x]]/100.0; else node[i].dis=node[i].dis*(xx[fr[node[i].x]]+xx[fr[node[i].y]])/200.0; } s=getint(),t=getint(); std::sort(node+1,node+1+m); for(int i=1;i<=tot;i++) father[i]=i; // for(int i=1;i<=m;i++) // printf("i=%d,x=%d,y=%d,dis=%.3lf\n",i,node[i].x,node[i].y,node[i].dis); int l,r; for(int i=m;i;i--){ int r1=find(node[i].x); int r2=find(node[i].y); if(r1==r2) continue; father[r1]=r2; if(find(s)==find(t)){ l=node[i].dis; for(int j=1;j<=tot;j++) father[j]=j; int idx; for(int j=i;j;j--){ if(node[j].dis<l) break; idx=j; } //printf("idx=%d\n",idx); int r1=find(node[i].x),r2=find(node[i].y); father[r1]=r2; if(find(s)==find(t)){ int q=node[i].dis; if(q-node[i].dis==0) r=l; else r=l+1; printf("%d %d\n",l,r); return 0; } for(int j=idx;j<=m;j++){ int r1=find(node[j].x); int r2=find(node[j].y); if(r1==r2) continue; father[r1]=r2; if(find(s)==find(t)){ int q=node[j].dis; if(q-node[j].dis==0) r=node[j].dis; else r=node[j].dis+1; printf("%d %d\n",l,r); return 0; } } } } }
T2
咕咕咕
T3 数学题,用van老师和特沈本教的方法推一波公式即可。注意开long long
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> const int N=1e7; #define ll long long #define int long long ll n; ll c[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("math.in","r",stdin);freopen("math.out","w",stdout); n=getint(); if(n==0 or n==1 or n==2){puts("0");return 0;} for(int i=1;i*i<=n;i++){ if(n%i) continue; if(n/i&1) c[++c[0]]=n-i; if(i*i!=n and i!=1 and i&1) c[++c[0]]=n-n/i; } std::sort(c+1,c+1+c[0]); printf("%lld ",c[0]); for(int i=1;i<=c[0];i++) printf("%lld ",c[i]); return 0; }
2018.8.10
开场看T1发现要求一条路径上线段的交的最大值,想了20min写了个自认为是正解的爆搜。然后看T2,想了会写了个Trie树,过了大样例及1w组对拍。此时刚刚过去一个半小时,T3显然不可做题,于是开始颓。
一个小时之后,想着测一下T1的极限数据,发现要跑12s,突然慌得一批。之后的一个小时因为想不出T1正解就一直在卡常,感觉只能拿30分,没想到直接得了90分,数据有点水(后来好像改数据T成70分了)
总分70+100+0=170,本部rank2,全场rank16(一堆省选组的神仙来虐场%%%)
T1 这东西枚举答案的左端点然后二分右端点bfs判断即可。
#include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 1005 #define M 3005 int n,m,cnt; int g[M<<1],tot; int head[N],vis[N]; struct Edge{ int to,nxt,l,r; }edge[M<<1]; void add(int x,int y,int a,int b){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].l=a; edge[cnt].r=b; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } bool check(int l,int r){ std::queue<int> q; q.push(1);memset(vis,0,sizeof vis);vis[1]=1; //printf("l=%d,r=%d\n",l,r); while(q.size()){ int u=q.front();q.pop();//printf("u=%d\n",u); for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(vis[to]) continue; if(edge[i].l>l or edge[i].r<r) continue; q.push(to);vis[to]=1;//printf("to=%d\n",to); } } return vis[n]; } void write(int x){ if(x>9) write(x/10); putchar(x%10+'0'); } signed main(){ freopen("travel.in","r",stdin);freopen("travel.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=m;i++){ int a=getint(),b=getint(),c=getint(),d=getint(); add(a,b,c,d);add(b,a,c,d); g[++tot]=c;g[++tot]=d; } g[++tot]=1; std::sort(g+1,g+1+tot); int len=std::unique(g+1,g+1+tot)-g-1; int now=0,nowl=0,nowr=0; for(int i=1;i<=len;i++){ int l=i,r=len,ans=0; while(l<=r){ if(g[r]-g[i]+1<now) break; int mid=l+r>>1; if(g[mid]-g[i]+1<now){ l=mid+1; continue; } if(check(g[i],g[mid])) ans=mid,l=mid+1; else r=mid-1; } if(!ans) continue; if(g[ans]-g[i]+1>now) now=g[ans]-g[i]+1,nowl=g[i],nowr=g[ans]; else if(g[ans]-g[i]+1==now and nowl>g[i]) nowl=g[i],nowr=g[ans]; } printf("%d\n",now); if(now) for(int i=nowl;i<=nowr;i++) write(i),putchar(' '); return 0; }
T2 这玩意儿还是挺水的trie树上维护信息就好了也比较好想,就是卡空间有点恶心
%:pragma GCC optimize(3) #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define re register const int inf=3e6+1; using std::max; int n,m,tot=1; int ch[inf][3]; char a[5000005]; int mx[inf],las[inf]; void ins(int x){ int len=strlen(a); int now=1; for(re int i=0;i<len;i++){ if(!ch[now][a[i]-'a']) ch[now][a[i]-'a']=++tot; now=ch[now][a[i]-'a']; mx[now]=max(mx[now],x-las[now]-1); las[now]=x; } } void query(){ int len=strlen(a); int now=1; for(re int i=0;i<len;i++){ if(!ch[now][a[i]-'a']){ printf("%d\n",n); return; } now=ch[now][a[i]-'a']; } int p=max(mx[now],n-las[now]); printf("%d\n",p); } signed main(){ freopen("word.in","r",stdin);freopen("word.out","w",stdout); scanf("%d%d",&n,&m); for(re int i=1;i<=n;i++){ scanf("%s",a); ins(i); } while(m--){ scanf("%s",a); query(); } return 0; }
T3
咕咕咕
B组
T1 分解质因数,二分一个答案mid,再求出1~mid中所有质数的指数,合法的条件当且仅当之前处理的每个质数的指数大于1~mid中质数的指数。
#include<cstdio> #include<cctype> #include<cstring> #define N 100005 int n,a[N]; int vis[N*10],pcnt; int p[N*10],c[N*10]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void init(){ for(int i=2;i<=1000000;i++){ if(!vis[i]) p[++pcnt]=i; for(int j=1;j<=pcnt;j++){ if(p[j]*i>1000000) break; vis[p[j]*i]=1; if(i%p[j]==0) break; } } } void fj(int x){ for(int i=1;i<=pcnt and x>=p[i];i++){ if(x%p[i]==0){ while(x%p[i]==0) c[i]++,x/=p[i]; } } } bool check(int x){ for(int i=1;i<=pcnt;i++){ int ans=0,now=1; for(int j=1;j;j++){ now=now*p[i]; if(now>x or now<0) break; ans=ans+x/now; } if(ans<c[i]) return 0; } return 1; } signed main(){ freopen("factorial.in","r",stdin); freopen("factorial.out","w",stdout); init(); n=getint(); for(int i=1;i<=n;i++) a[i]=getint(),fj(a[i]); int l=1,r=N*100,ans; while(l<=r){ int mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }
T2 辣鸡BFS,更辣鸡的我调了一个小时
%:pragma GCC optimize(2) #include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 505 #define re register using std::min; int n,m; int mp[105]; char ch[N][N]; int dis[N][N][4]; int vis[N][N][4]; int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; struct Node{ int x,y,dir,dis; friend bool operator<(Node a,Node b){ return a.dis>b.dis; } }; std::priority_queue<Node> q; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("run.in","r",stdin); freopen("run.out","w",stdout); mp['U']=0;mp['D']=2;mp['L']=1;mp['R']=3; n=getint(),m=getint(); for(re int i=1;i<=n;i++) scanf("%s",ch[i]+1); q.push((Node){1,1,-1,0}); memset(dis,0x3f,sizeof dis); while(q.size()){ Node u=q.top();q.pop(); int x=u.x,y=u.y,dir=u.dir; if(x==n and y==m){ printf("%d\n",dis[x][y][dir]); return 0; } if(dir!=-1 and vis[x][y][dir]) continue; if(dir!=-1) vis[x][y][dir]=1; if(ch[x][y]=='S') continue; if(dir==-1){ for(re int i=0;i<4;i++){ if(i==mp[ch[x][y]]) continue; int nx=x+dx[i]; int ny=y+dy[i]; if(nx<1 or ny<1 or nx>n or ny>m) continue; dis[nx][ny][i]=0; q.push((Node){nx,ny,i,0}); } continue; } for(re int i=0;i<4;i++){ if(i==mp[ch[x][y]]) continue; int nx=x+dx[i]; int ny=y+dy[i]; if(nx==1 and ny==1) continue; if(nx<1 or ny<1 or nx>n or ny>m) continue; if(dis[nx][ny][i]<=dis[x][y][dir]+(i!=dir)) continue; dis[nx][ny][i]=dis[x][y][dir]+(i!=dir); q.push((Node){nx,ny,i,dis[nx][ny][i]}); } } printf("No Solution"); return 0; }
T3
咕咕咕
2018.8.24
T1 看到边权是1应该直接BFS的求个毛线的最短路啊。。
%:pragma GCC optimize(2) #include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define K 105 #define N 100005 using std::max; using std::min; using std::swap; int head[N],dis[N]; int n,m,k,cnt,in[N]; int ok[K],ans[N],vis[N]; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void dij(int s){ std::queue<int> q;q.push(s); memset(dis,0x3f,sizeof dis);dis[s]=0; while(q.size()){ int u=q.front();q.pop();in[u]=0; ans[u]=max(ans[u],dis[u]); for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(dis[to]>dis[u]+1){ dis[to]=dis[u]+1; if(!in[to]) in[to]=1,q.push(to); } } } } signed main(){ freopen("oasis.in","r",stdin);freopen("oasis.out","w",stdout); n=getint(),m=getint(),k=getint(); for(int i=1;i<=k;i++) ok[i]=getint(); for(int i=1;i<=m;i++){ int a=getint(),b=getint(); add(a,b);add(b,a); } for(int i=1;i<=k;i++) dij(ok[i]); for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }
T2 考试的时候一直以为这是道数学题
定义 f[i][j] 表示有 i 个一类点,j 个二类点且都连满的方案数
为了防止重复,规定要先连一类点再连二类点,也就是说 $f[i+1][j]+=f[i-1][j]*i$ 要在 $i>0\;\& \&\;j==0$ 的情况下
另外枚举放一个二类点和哪些点连边,dp转移即可
#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 2005 #define int long long const int mod=998244353; using std::max; using std::min; using std::swap; int f[N][N]; int n,cnt1,cnt2; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("map.in","r",stdin);freopen("map.out","w",stdout); n=getint(); for(int i=1;i<=n;i++){ int x=getint(); if(x==1) cnt1++;else cnt2++; } f[0][0]=1; for(int tot=0;tot<=n;tot++){ for(int i=n;~i;i--){ int j=tot-i; if(j<0) continue; if(i and j==0) (f[i+1][j]+=f[i-1][j]*i%mod)%=mod; if(i>1) (f[i][j+1]+=f[i-2][j]*(i*(i-1)/2)%mod)%=mod; if(i and j) (f[i][j+1]+=f[i][j-1]*i%mod*j%mod)%=mod; if(j>1) (f[i][j+1]+=f[i+2][j-2]*(j*(j-1)/2)%mod)%=mod; } } printf("%lld\n",f[cnt1][cnt2]); return 0; }
T3 这东西挺简单的考场上式子就差一步就推过来了啊。。
定义sum[i..j]为i到j的区间和。
假设当前考虑了第1—i个数,答案不在其中。那么下一个可能是答案的数一定大于等于sum[1..i],设第一个大于等于sum[1..i]的数在第x个(可以使用线段树二分找到)。判断第x个数是否是答案,如果是就退出,否则把未找到答案的范围扩展到第1—x个数,此时值域至少乘2,所以只要进行log次就找到答案或返回无解。
#include<cstdio> #include<cctype> #include<cstring> #include<iostream> using std::max; using std::min; using std::swap; #define N 200005 #define ls cur<<1,l,mid,ql,qr #define rs cur<<1|1,mid+1,r,ql,qr int n,m; int p[N],tag[N<<2]; int mx[N<<2],sum[N<<2]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void pushup(int cur){ sum[cur]=sum[cur<<1]+sum[cur<<1|1]; mx[cur]=max(mx[cur<<1],mx[cur<<1|1]); } void build(int cur,int l,int r){ if(l==r){ sum[cur]=mx[cur]=p[l]; return; } int mid=l+r>>1; build(cur<<1,l,mid);build(cur<<1|1,mid+1,r); pushup(cur); } int querysum(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return sum[cur]; int mid=l+r>>1,ans=0; if(ql<=mid) ans+=querysum(ls); if(mid<qr) ans+=querysum(rs); return ans; } void modify(int cur,int l,int r,int ql,int qr,int x){ if(ql<=l and r<=qr){ sum[cur]=mx[cur]=x; return; } int mid=l+r>>1; if(ql<=mid) modify(ls,x); else modify(rs,x); pushup(cur); } int query(int cur,int l,int r,int ql,int qr,int x){ if(mx[cur]<x) return -1; if(l==r){ if(mx[cur]>=x) return l; else return -1; } int mid=l+r>>1,ans=-1; if(ql<=mid) ans=query(ls,x); if(ans!=-1) return ans; if(mid<qr) ans=query(rs,x); return ans; } signed main(){ freopen("challenge.in","r",stdin);freopen("challenge.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=n;i++) p[i]=getint(); build(1,1,n); while(m--){ int a=getint(),b=getint(); modify(1,1,n,a,a,b);p[a]=b; if(!p[1]){ printf("1\n"); continue; } int now=p[1],at=2; while(at<=n){ int pp=query(1,1,n,at,n,now); // printf("pp=%d\n",pp); if(pp==-1){ printf("-1\n"); break; } else{ int q=querysum(1,1,n,1,pp-1); if(q==p[pp]){ printf("%d\n",pp); break; } at=pp+1;now=q+p[pp]; } } } return 0; }
2018.8.27
T1 一个性质是联通块数=点数-你的边数。然而我连这个性质都没意识到=。= 。然后推一波式子之后发现这题跟阿狸和桃子的游戏一样是个贪心,交替选度数最小的点即可。
代码丢了就不贴了
T2 挺裸的矩乘啊 然而只有我tblztb切了
代码找不到了就不贴了
T3 不会滚
2018.9.10~2018.9.11
靠最近老是考这么差,再这样下去noip要省二滚粗了啊。。
Day1
T1
把式子写出来是 $c_i=\sum \limits_{j=1}^i a_{\lfloor{\frac{i}{j}}\rfloor}b_{i-\lfloor{\frac{i}{j}}\rfloor\times j}$
发现a的下标可以数论分块,b的下标可以前缀和搞一下,定义qzh[i][j]表示从点i往前每j个元素统计一次的前缀和,那么后面那个b就是i-kj,i-(k+1)j,i-(k+2)j...i-rj,可以用前缀和弄成 qzh[i-kj][k]-qzh[i-(r+1)*k][k]。
空间不够?分块思想
k大于根号n的暴力求,小于根号n的再用前缀和
这个思想还是很重要的,考场上前缀和啥的都想到了就差这一步了啊啊啊GG
#include<cmath> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #include<algorithm> #define N 100005 using std::min; using std::max; using std::swap; #define ll long long const int mod=123456789; int n,a[N],b[N]; int qzh[N][400]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ n=getint();int block=sqrt(n); for(int i=1;i<=n;i++) a[i]=getint()%mod; for(int i=1;i<=n;i++) b[i-1]=getint()%mod; for(int j=1;j<=block;j++){ for(int i=0;i<n;i++) qzh[i][j]=((i>=j?qzh[i-j][j]:0)+b[i])%mod; } for(int i=1;i<=n;i++){ ll ans=0; for(int j=1,r;j<=i;j=r+1){ int p=i/j; r=i/p; if(p>block){ for(int k=j;k<=r;k++) ans+=(ll)a[p]*b[i%k]%mod; } else ans+=(ll)a[p]*(qzh[i-p*j][p]-(i>=(r+1)*p?qzh[i-(r+1)*p][p]:0)+mod)%mod; } printf("%lld\n",ans%mod); } return 0; }
Day2
T1
这么水的题考场愣是没想出来
对于一个坐标为d,高为h的仙人掌,一定要在d-h之前跳,同时一定会在d+h之后落地。那么一个个仙人掌就转化成了一段段区间,因为不能二段跳,所以求一段最长区间并就行了。
考场上过于想知道恐龙在某个点的状态会是什么,忽略了一些小性质。
#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #include<algorithm> #define N 300005 using std::min; using std::max; using std::swap; int n; struct Node{ int l,r; friend bool operator<(Node x,Node y){ return x.l<y.l or x.l==y.l and x.r>y.r; } }node[N],node2[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ n=getint(); for(int i=1;i<=n;i++){ int a=getint(),b=getint(); if(a<b) return printf("-1"),0; node2[i].l=a-b;node2[i].r=a+b; } std::sort(node2+1,node2+1+n); int now=0; for(int i=1;i<=n;i++){ if(now and node2[i].l==node[now].l) continue; node[++now]=node2[i]; } n=now; int maxn=0,l=node[1].l,r=node[1].r; for(int i=2;i<=n;i++){ if(node[i].l<r) r=max(r,node[i].r); else maxn=max(maxn,r-l),l=node[i].l,r=node[i].r; } maxn=max(maxn,r-l); printf("%.1lf\n",(double)maxn/2.0); return 0; }
T2
水
T3
直接按联通块的size从小到大dfs,如果两个联通块一样大就先dfs因数个数少的那个。第二条剪枝极为有用。
#include<cstdio> #include<cctype> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define N 10 using std::min; using std::max; using std::swap; #define re register struct Node{ int x,y; }; std::vector<int> v[100000]; std::vector<Node> ltk[N*N]; int dx[]={-1,0,1,0},dy[]={0,1,0,-1}; int n,want[N][N],mp[N][N],tot,idx[N][N],cnt; int ans[N][N],hang[N],lie[N],now[N*N],vis[N][N]; bool cmpp(std::vector<Node> a,std::vector<Node> b){ return a.size()<b.size() or a.size()==b.size() and v[want[a[0].x][a[0].y]].size()<v[want[b[0].x][b[0].y]].size(); } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void dfs(int x,int y,int z){ ltk[z].push_back((Node){x,y}); idx[x][y]=z;vis[x][y]=1; for(int k=0;k<4;k++){ int nx=x+dx[k],ny=y+dy[k]; if(nx<1 or ny<1 or nx>n or ny>n or want[nx][ny]!=want[x][y] or vis[nx][ny]) continue; dfs(nx,ny,z); } } void cmp(){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(ans[i][j]!=mp[i][j]){ if(ans[i][j]<mp[i][j])return; else{ for(int k=1;k<=n;k++){ for(int p=1;p<=n;p++) ans[k][p]=mp[k][p]; } return; } } } } } void print(){ for(int i=1;i<=n;i++,puts("")){ for(int j=1;j<=n;j++) printf("%d ",mp[i][j]); } } void sdfs(int x,int xx){ // printf("x=%d,xx=%d\n",x,xx); // print();puts(""); if(x>cnt){ tot++;cmp(); return; } if(xx>=ltk[x].size()){ if(now[x]!=1) return; sdfs(x+1,0); return; } Node y=ltk[x][xx]; re int p=now[x],pp=hang[y.x]|lie[y.y]; for(int k=0;k<v[p].size();k++){ int z=v[p][k]; re int q=1<<z-1; if(pp&q) continue; mp[y.x][y.y]=z;hang[y.x]=hang[y.x]|q;lie[y.y]=lie[y.y]|q;now[x]=now[x]/z; sdfs(x,xx+1); mp[y.x][y.y]=0;hang[y.x]=hang[y.x]^q;lie[y.y]=lie[y.y]^q;now[x]=now[x]*z; } } signed main(){ for(int i=1;i<=9;i++){ for(int k=1;;k++){ if(i*k>100000) break; v[i*k].push_back(i); } } n=getint(); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) want[i][j]=getint(); } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(!vis[i][j]) dfs(i,j,++cnt); } } std::sort(ltk+1,ltk+1+cnt,cmpp); for(int i=1;i<=cnt;i++) now[i]=want[ltk[i][0].x][ltk[i][0].y]; memset(ans,0x3f,sizeof ans); sdfs(1,0); printf("%d\n",tot); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) printf("%d ",ans[i][j]); puts(""); } return 0; }
2018.9.20
IOI赛制就很刺激。开场思考T3无果,发现一堆人A了T1,感觉是傻逼题。转去想T1更无果。此时1.5h过去,得分还是0分,心态非常爆炸。
然后去想T2,突然想起正难则反。然后有了点思路,A了T2。又写了T3的暴力,又想了1hT1,。发现确实是傻逼题,又切了T1。
总分100+100+40=240.本部rank3 (狗逼特神犇最后2min交了个暴力把我卡下去了mmp)。
T1
可以取一个>1e9的大质数,然后用大质数减去得出的结果肯定是P的倍数。然后用这个减去1询问一下就是P-1了。再加上1就是P了。
#include "test.h" int Solve(){ int x=1e9+7,y=Query(x); int z=Query(x-y-1); return z+1; }
T2
正难则反。我们考虑求出一个序列,让它们的GCD不等于1的方案数。然后用m^n减去这个方案就行了。然后发现,这是一个递归的子问题,就可以递归求了。复杂度O(logn*sqrt(n))
老器说我这是杜教筛?可是我从来没写过啊原理也不知道。。我又xjbYY出了一个算法??
#include<map> #include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; #define pii std::pair<ull,ull> typedef unsigned long long ull; std::map<pii,ull> mp; #define mp(A,B) std::make_pair(A,B) int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } ull ksm(ull a,ull b){ ull ans=1; while(b){ if(b&1) ans=ans*a; a=a*a;b>>=1; } return ans; } ull solve(ull x,ull y){ pii qqq=mp(x,y); if(mp[qqq]) return mp[qqq]; // std::cout<<x<<" "<<y<<std::endl; if(y==1) return 1ULL; ull p=ksm(y,x),ans=0; for(ull i=2,r;i<=y;i=r+1){ ull j=y/i;r=y/j; ans+=solve(x,j)*(r-i+1); } // for(ull i=2;i<=y;i++) // ans+=solve(x,y/i); return mp[qqq]=p-ans; } signed main(){ ull n,m;std::cin>>n>>m; std::cout<<solve(n,m); return 0; }
T3
咕咕咕
2018.9.22
这场比赛全程心态比较好。最开始他们噼里啪啦开始敲之后也没有很着急,也是自己在那一点一点推。然后先切了T1,再切了T3。然后也没有太满足感觉大众分都是这个分就又去好好想了想T2。一眼看出是性质题,在纸上写下“推性质”,想了大概一个半小时突然有了一个想法,然后写了一个非常优秀的暴力。
总分100+80+100=280 rank1 今天心态比较好吧没做出来的题没有着急做出来后也没有放松。再加上他们四个没发挥出正常水平就rank1了。
T1
第一个性质是跳的楼房的高度肯定是单调的不然可以通过交换变得更优。然后暴力O(n^3)dp就行了
#include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int N=55; using std::min; using std::max; using std::swap; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int n,m,g[N],len; ll f[N][N][N];//f[i][j][k]->前i个 选了j个 最后一个选的是k 最小多少 struct Node{ int c,h; friend bool operator<(Node x,Node y){ return x.h<y.h; } }node[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } signed main(){ n=getint(); for(int i=1;i<=n;i++) node[i].c=getint(); for(int i=1;i<=n;i++) node[i].h=getint(),g[i]=node[i].h; m=getint(); std::sort(node+1,node+1+n); std::sort(g+1,g+1+n);len=std::unique(g+1,g+1+n)-g-1; for(int i=1;i<=n;i++) node[i].h=std::lower_bound(g+1,g+1+len,node[i].h)-g; for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ for(int k=0;k<=n;k++) f[i][j][k]=2e18; } } f[0][0][0]=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ for(int k=0;k<i;k++){ //f[i-1][j-1][k] f[i][j][k]=min(f[i-1][j][k],f[i][j][k]); if(j!=1) f[i][j][i]=min(f[i][j][i],f[i-1][j-1][k]+(ll)node[i].c+g[node[i].h]-g[node[k].h]); else f[i][j][i]=min(f[i][j][i],(ll)node[i].c); } // printf("i=%d,j=%d,f=%lld\n",i,j,f[i][j][i]); } } int ans=0; for(int j=1;j<=n;j++){ for(int k=1;k<=n;k++){ if(f[n][j][k]<=m) ans=max(ans,j); } } printf("%d\n",ans); return 0; }
T2
吼性质题啊!
考场上脑抽一直觉得除了a2+a3还要考虑诸如a2+a4等等的情况觉得很不可做所以只枚举了a1
实际上知道了a2+a3之后就知道了a1,进而就知道了a4~an
用multiset存一下就行了
#include<set> #include<vector> #include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; const int N=305; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) std::multiset<int> s; int n,b[N*N],a[N],ans; std::vector<int> v[100005]; std::multiset<int>::iterator it; bool cmp(std::vector<int> a,std::vector<int> b){ for(int i=0;i<n;i++){ if(a[i]!=b[i]) return a[i]>b[i]; } return 1; } int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } void dfs(int now){ if(now>n){ ans++; for(int i=1;i<=n;i++) v[ans].pb(a[i]); return; } a[now]=(*s.begin())-a[1]; for(int i=1;i<now;i++){ it=s.lower_bound(a[i]+a[now]); if(it==s.end() or (*it)!=a[i]+a[now]){ for(int j=1;j<i;j++) s.insert(a[j]+a[now]); return; } s.erase(it); } dfs(now+1); for(int i=1;i<now;i++) s.insert(a[i]+a[now]); } signed main(){ n=getint(); for(int i=1;i<=n*(n-1)/2;i++) b[i]=getint(),s.insert(b[i]); std::sort(b+1,b+1+n*(n-1)/2); for(int i=3;i<=n;i++){ //a[2]+a[3]=b[i] a[1]=b[1]+b[2]-b[i]>>1; a[2]=b[1]-a[1];a[3]=b[2]-a[1]; it=s.lower_bound(b[1]);s.erase(it); it=s.lower_bound(b[2]);s.erase(it); it=s.lower_bound(b[i]);s.erase(it); dfs(4); s.insert(b[1]);s.insert(b[2]);s.insert(b[i]); } std::sort(v+1,v+1+ans,cmp); ans=std::unique(v+1,v+1+ans)-v-1; printf("%d\n",ans); for(int i=1;i<=ans;i++){ for(int j=0;j<n;j++) printf("%d ",v[i][j]); puts(""); } return 0; }
T3
值域只有10000,可以按值域分块做。
就是都想到离线排序了>100的还去用主席树做我大概是个zz
那个>100的也可以弄成前缀相减的形式啊好有道理啊
#include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; typedef long long ll; #define ls ch[cur][0] #define rs ch[cur][1] #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int qzh[105]; int ans[N],tot,cnt; int n,m,maxn,val[N]; int root[N],sum[N*30],ch[N*30][2]; struct Ques{ int l,r,p,v,idx,type; friend bool operator<(Ques x,Ques y){ if(x.p!=y.p) return x.p<y.p; return x.l<y.l; } }ques[N<<1]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } void build(int &cur,int l,int r){ if(!cur) cur=++cnt; if(l==r) return; int mid=l+r>>1; build(ls,l,mid);build(rs,mid+1,r); } int modify(int pre,int l,int r,int ql,int qr){ int cur=++cnt;ch[cur][0]=ch[pre][0];ch[cur][1]=ch[pre][1];sum[cur]=sum[pre]+1; if(l==r) return cur; int mid=l+r>>1; if(ql<=mid) ch[cur][0]=modify(ch[pre][0],l,mid,ql,qr); else ch[cur][1]=modify(ch[pre][1],mid+1,r,ql,qr); return cur; } int query(int pre,int now,int l,int r,int ql){ if(l==r) return sum[now]-sum[pre]; int mid=l+r>>1; if(ql<=mid) return query(ch[pre][0],ch[now][0],l,mid,ql); else return query(ch[pre][1],ch[now][1],mid+1,r,ql); } signed main(){ // freopen("in.txt","r",stdin); n=getint(),m=getint(); for(int i=1;i<=n;i++) val[i]=getint(),maxn=max(maxn,val[i]); for(int i=1;i<=m;i++){ int l=getint(),r=getint(),p=getint(),v=getint(); if(p<=100){ ques[++tot].l=l-1;ques[tot].r=l-1;ques[tot].p=p;ques[tot].v=v;ques[tot].idx=i;ques[tot].type=-1; ques[++tot].l=r;ques[tot].r=r;ques[tot].p=p;ques[tot].v=v;ques[tot].idx=i;ques[tot].type=1; } else{ ques[++tot].l=l;ques[tot].r=r;ques[tot].p=p;ques[tot].v=v;ques[tot].idx=i;ques[tot].type=2; } } std::sort(ques+1,ques+1+tot); build(root[0],0,maxn); for(int i=1;i<=n;i++) root[i]=modify(root[i-1],0,maxn,val[i],val[i]); for(int i=1;i<=tot;){ if(ques[i].p<=100){ if(i==1 or ques[i].p!=ques[i-1].p){ memset(qzh,0,sizeof qzh); int now=i; while(now<=tot and ques[now].l==0 and ques[now].p==ques[i].p) now++; if(ques[now].p!=ques[i].p){ i=now; continue; } for(int j=1;j<=n;j++){ qzh[val[j]%ques[i].p]++; while(now<=tot and ques[now].l==j and ques[now].p==ques[i].p){ ans[ques[now].idx]+=ques[now].type*qzh[ques[now].v]; now++; } } i=now;continue; } } else{ // printf("ques.p=%d\n",ques[i].p); int now=ques[i].v; while(1){ if(now>maxn) break; ans[ques[i].idx]+=query(root[ques[i].l-1],root[ques[i].r],0,maxn,now); // printf("now=%d,ans=%d\n",now,ans[ques[i].idx]); now+=ques[i].p; } i++; } } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
2018.9.26
md三道傻逼题全场切然而T3调试完多删了一句话导致没有输出我就是个zz
总分100+100+0=200 rank10
傻逼死了我
T2
题面保证相同的数字小于等于10个肯定是个突破口
然后因为两段相同的话那么相对的数字的位置肯定是一样的
就可以暴力存一下两两相同字符之间的距离,然后枚举匹配的长度,哈希判一下就行了。用了双模数,比较稳。
#include<set> #include<map> #include<queue> #include<cctype> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; typedef long long ll; const ll mod1=1e9+7; const ll mod2=1e9+9; const ll base=100007; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) std::vector<int> v[N]; int n,val[N],g[N],len,las[N][12]; ll hsh1[N],hsh2[N],len1[N],len2[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } bool ok(int s,int t,int len){ ll a=(hsh1[t-1]-hsh1[s-1]*len1[len]%mod1+mod1)%mod1; ll b=(hsh1[t+len-1]-hsh1[t-1]*len1[len]%mod1+mod1)%mod1; if(a!=b) return 0; a=(hsh2[t-1]-hsh2[s-1]*len2[len]%mod2+mod2)%mod2; b=(hsh2[t+len-1]-hsh2[t-1]*len2[len]%mod2+mod2)%mod2; if(a!=b) return 0; return 1; } signed main(){ freopen("dor.in","r",stdin);freopen("dor.out","w",stdout); n=getint();len1[0]=len2[0]=1; for(int i=1;i<=n;i++){ g[i]=val[i]=getint(); len1[i]=len1[i-1]*base%mod1; len2[i]=len2[i-1]*base%mod2; } std::sort(g+1,g+1+n);len=std::unique(g+1,g+1+n)-g-1; for(int i=1;i<=n;i++){ val[i]=std::lower_bound(g+1,g+1+len,val[i])-g; hsh1[i]=(hsh1[i-1]*base%mod1+val[i])%mod1; hsh2[i]=(hsh2[i-1]*base%mod2+val[i])%mod2; las[val[i]][++las[val[i]][0]]=i; for(int j=1;j<las[val[i]][0];j++) v[i-las[val[i]][j]].pb(i); } int l=1; for(int len=1;len<=n/2;len++){ for(int i=0;i<v[len].size();i++){ if(v[len][i]-len<l) continue; if(v[len][i]+len-1>n) continue; // printf("len=%d,v=%d\n",len,v[len][i]); if(ok(v[len][i]-len,v[len][i],len)) l=v[len][i]; } } printf("%d\n",n-l+1); for(int i=l;i<=n;i++) printf("%d ",g[val[i]]); return 0; }
2018.9.29
啊昨天的不想写了好烦啊
写今天的吧。考试过程啥的也不想写了。md二分边界写错少了50pts搁谁都不想写啊
T1 %%%yjc15min急速A题 狗题居然卡空间卡了我半个小时才卡过去
假设|s|<|t|。观察一下s每个位置都会在t的哪里出现。发现间隔是gcd(|s|,|t|)。然后前缀和搞一下就行了。
~~说着比较简单然而做了我1.5h还卡了0.5h空间~~
#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=1000005; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) ll n,m; ll lena,lenb; ll qzh[N][27]; char s[N],t[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } signed main(){ // freopen("a.txt","r",stdin); freopen("string.in","r",stdin);freopen("string.out","w",stdout); n=getint(),m=getint(); scanf("%s%s",s+1,t+1); lena=strlen(s+1);lenb=strlen(t+1); if(lena>lenb) swap(lena,lenb),swap(s,t),swap(n,m); ll g=gcd(lena,lenb); ll d=lena*lenb/g; for(int i=1;i<=lenb;i++){ for(int j=1;j<=26;j++){ if(i>g) qzh[i][j]=qzh[i-g][j]+(t[i]-'a'+1==j); else qzh[i][j]=(t[i]-'a'+1==j); } } ll ans=0,now=(lenb-1)/g*g+1; for(int i=1;i<=lena;i++){ if(i+now-1<=lenb){ ans+=qzh[i+now-1][s[i]-'a'+1]-(i>g?qzh[i-g][s[i]-'a'+1]:0); } else{ ans+=qzh[(i+now-1)%lenb][s[i]-'a'+1]+qzh[i+now-1-(i+now-1-lenb+g-1)/g*g][s[i]-'a'+1]-(i>g?qzh[i-g][s[i]-'a'+1]:0); } } printf("%lld\n",n*lena/d*ans); return 0; }
T2 md就是这题二分边界写错少了50pts无缘rank2
首先观察到可以二分。然后枚举哪个是最高的积木。观察到这个最高的两边呈等差数列,然后在左边找个最大的右边找个最小的大于x的值就行了。
这玩意儿可以拿单调队列做?考场上时间比较紧张直接上的线段树,然而因为一些奇怪的原因并没有过大样例。
下来调了调发现是线段树直接区间查会有锅,改了一下感觉复杂度不对但是过了?
#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; #define int long long const int N=100005; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) #define ls cur<<1 #define rs cur<<1|1 #define lss ls,l,mid,ql,qr #define rss rs,mid+1,r,ql,qr int n,m,val[N]; ll sum[N],sum2[N],f[N]; ll mx[N<<2][2],idx[N<<2][2]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } const int inf=2e16; int query(int cur,int l,int r,int ql,int qr,int c,int pd){ if(mx[cur][pd]<c) return inf+5; if(l==r) return l; int mid=l+r>>1; if(qr<=mid) return query(lss,c,pd); if(ql>mid) return query(rss,c,pd); if(!pd){ int a=query(rss,c,pd); if(a!=inf+5) return a; return query(lss,c,pd); } else{ int a=query(lss,c,pd); if(a!=inf+5) return a; return query(rss,c,pd); } } bool check(ll mid){ for(int i=1;i<=n;i++){ if(val[i]>=mid) return 1; //find l r ll l=query(1,1,n,1,i-1,mid-i,0)+1,r=query(1,1,n,i+1,n,mid+i,1)-1; if(l>=inf or r>=inf) continue; ll used1=0,used2=0; used1=(mid+i)*(r-i+1)-sum[r]+sum[i-1]; used2=(mid-i)*(i-l+1)+sum2[i]-sum2[l-1]; if(used1+used2-(mid-val[i])<=m) return 1; } return 0; } void modify(int cur,int l,int r,int ql,int qr,ll c,ll d){ if(l==r){ mx[cur][0]=d; mx[cur][1]=c; idx[cur][0]=idx[cur][1]=l; return; } int mid=l+r>>1; if(ql<=mid) modify(lss,c,d); else modify(rss,c,d); mx[cur][0]=max(mx[ls][0],mx[rs][0]); if(mx[cur][0]==mx[rs][0]) idx[cur][0]=idx[rs][0]; else idx[cur][0]=idx[ls][0]; mx[cur][1]=max(mx[ls][1],mx[rs][1]); if(mx[cur][1]==mx[ls][1]) idx[cur][1]=idx[ls][1]; else idx[cur][1]=idx[rs][1]; } signed main(){ // freopen("in.txt","r",stdin); // freopen("block.in","r",stdin);freopen("block.out","w",stdout); n=getint();m=getint(); for(int i=1;i<=n;i++){ val[i]=getint(); modify(1,1,n,i,i,val[i]+i,val[i]-i); sum[i]=sum[i-1]+val[i]+i; sum2[i]=sum2[i-1]+i-val[i]; } ll l=0,r=2e18,ans=0; while(l<=r){ ll mid=l+r>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%lld\n",ans); return 0; }
2018.9.30
hin良心的一套题
做了一个半小时发现C题是一道防AK的题然而暴力有70于是懒得去想正解了。。
总分100+100+70=270 rank1
T3
考场上就感觉跟之前CF的一道bubble cup的题很像,然而并没有往下细想。
其实很水,我们维护一下当前栈里的元素的哈希值,然后在每个位置找一下相同的哈希值之前出现过了几次,加上这个答案就行。因为在之前的某个位置和现在是相同的字符的话,那中间的这段一定是消掉的,直接加这段区间就好了。然而这题卡常幸亏没写正解不然跑成跟暴力一个分怕是要气死
#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=1000005; typedef long long ll; const int base=127; const int mod1=1e9+7; const int mod2=1e9+9; #define pb(A) push_back(A) #define pii std::pair<ll,ll> #define mp(A,B) std::make_pair(A,B) int n; char ch[N]; int stk[N],top; struct Node{ int x,y; ll get(){ return 1ll*x*mod2+y; } friend bool operator<(Node a,Node b){ return a.x<b.x or a.x==b.x and a.y<b.y; } }H[N]; std::map<ll,int> mp; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } signed main(){ scanf("%s",ch+1);n=strlen(ch+1); ll ans=0;H[0].x=0;H[0].y=0;mp[H[0].get()]++; for(int i=1;i<=n;i++){ if(top and stk[top]==(int)ch[i]) top--; else{ stk[++top]=ch[i]; H[top]=H[top-1]; H[top].x=(1ll*H[top].x*base+ch[i])%mod1; H[top].y=(1ll*H[top].y*base+ch[i])%mod2; } ll x=H[top].get(); ans+=mp[x];mp[x]++; } printf("%lld\n",ans); return 0; }
2018.10.9
从头被吊打到尾
开场旁边的yqy直接惊呼“这不原题么”然后噼里啪啦一顿乱敲,顿时心态非常爆炸。知道大概是个贪心也知道是个原题貌似也写过然而就是想不起来怎么做,无奈老老实实自己想。想了个贪心之后忘了有大样例直接敲了个对拍。然后拍一组wa一组,最后修修补补的总算把贪心给写对了。然后去看T2,冥思一小时后想到了正解感觉可以拿莫队做,因为n,m加减都是可以O(1)算出来的,无奈考场犯傻逼算错复杂度觉得莫队复杂度是O(nm sqrt(nm))=O(n^3)不如暴力,于是写了80分部分分还写挂了10分去看T3。T3想了半天大概有正解的思路了就是各种vecotr二分乱搞然而觉得太复杂了就没写,然后去写部分分,然而这次也是写了80分部分分然而写挂了40分。差分之后求前缀和就扫一遍也真是没谁了
总分100+70+40=210=GG
靠靠靠暴力不打错就rank4了暴力打满就rank2了啊啊啊血亏
T2 真是没救了能否定想出来的正解我甚至开始佩服自己的傻逼了
#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; const int M=100000; const int mod=1e9+7; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int fac[N],ifac[N],block,ans[N]; struct Ques{ int l,r,idx; friend bool operator<(Ques a,Ques b){ return a.l/block<b.l/block or a.l/block==b.l/block and a.r<b.r; } }ques[N]; int C(int n,int m){ return (ll)fac[n]*ifac[n-m]%mod*ifac[m]%mod; } int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } int ksm(int a,int b){ int ans=1; while(b){ if(b&1) ans=(ll)ans*a%mod; a=(ll)a*a%mod;b>>=1; } return ans; } signed main(){ fac[0]=ifac[0]=1;block=100; for(int i=1;i<=M;i++) fac[i]=(ll)fac[i-1]*i%mod; ifac[M]=ksm(fac[M],mod-2); for(int i=M-1;i;i--) ifac[i]=(ll)ifac[i+1]*(i+1)%mod; getint();int q=getint(); for(int i=1;i<=q;i++){ ques[i].l=getint();ques[i].r=getint();ques[i].idx=i; } std::sort(ques+1,ques+q+1); int l=0,r=0,nowans=1,inv=ksm(2,mod-2); for(int i=1;i<=q;i++){ // printf("q=%d,l=%d,r=%d,ans=%d\n",q,l,r,nowans); if(r<ques[i].r){ while(l<ques[i].l){l++;nowans=((ll)nowans*2%mod-C(l-1,r)+mod)%mod;} while(l>ques[i].l){l--;nowans=((ll)nowans+C(l,r))%mod*inv%mod;} while(r<ques[i].r){r++;nowans=((ll)nowans+C(l,r))%mod;} } else if(r>ques[i].r){ while(r>ques[i].r){nowans=((ll)nowans-C(l,r)+mod)%mod;r--;} while(l<ques[i].l){l++;nowans=((ll)nowans*2%mod-C(l-1,r)+mod)%mod;} while(l>ques[i].l){l--;nowans=((ll)nowans+C(l,r))%mod*inv%mod;} } else{ while(l<ques[i].l){l++;nowans=((ll)nowans*2%mod-C(l-1,r)+mod)%mod;} while(l>ques[i].l){l--;nowans=((ll)nowans+C(l,r))%mod*inv%mod;} } ans[ques[i].idx]=nowans; } for(int i=1;i<=q;i++) printf("%d\n",ans[i]); return 0; }
T3 改不动告辞
2018.10.10
这里是空间开大选手yzh
总分100+0+30=130 啊啊啊好亏啊T2要开log的空间脑抽怕RE多摁了两个0...
T1 贪心的想一定是删去最小的a的一段和最小的b的一段,双指针扫一下就行了 考场上没清空数组调了我二十分钟
#include<set> #include<map> #include<cctype> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; typedef long long ll; int n,m; int cx[N]; struct Node{ int val,idx; friend bool operator<(Node x,Node y){ return x.val<y.val; } }a[N],b[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); if(f) return -x;return x; } signed main(){ freopen("d.in","r",stdin);freopen("d.out","w",stdout); int T=getint(); while(T--){ n=getint();m=getint(); for(int i=1;i<=n;i++){ a[i].val=getint(); b[i].val=getint(); a[i].idx=b[i].idx=i; } std::sort(a+1,a+1+n);std::sort(b+1,b+1+n); ll ans=(ll)a[1].val*b[1].val; for(int i=1;i<=m;i++) cx[a[i].idx]++; int nowa=m+1,nowb=1; while(nowb<=n and cx[b[nowb].idx]){ cx[b[nowb].idx]++; nowb++; } ans=max(ans,(ll)a[m+1].val*b[nowb].val); int las=a[m+1].val; for(int i=m;i;i--){ cx[a[i].idx]--; if(!cx[a[i].idx]){ cx[b[nowb].idx]++;nowb++; while(nowb<=n and cx[b[nowb].idx]){ cx[b[nowb].idx]++; nowb++; } las=a[i].val; } ans=max(ans,(ll)las*b[nowb].val); } printf("%lld\n",ans); memset(cx,0,sizeof cx); } return 0; }
T2 这个最小的联通块肯定是每个点到lca组成的链的集合 主席树暴搞求一个值的前驱后继就好了
然而考场没想出来一个log的做法就外层套了个二分。。
然后因为这个二分的做法还要额外记录一下dfs时候的临时变量,然后这个需要logn的空间
but我开成了根号级别
#include<set> #include<map> #include<cctype> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; #define re register typedef double db; const int N=100005; typedef long long ll; int n,q,type,cnt,tot; int sum[N*40],ch[N*40][2]; int f[N][19],len,d[N],g[N]; int val[N],head[N],root[N]; struct Edge{ int to,nxt; }edge[N<<1]; inline void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } inline int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); if(f) return -x;return x; } int modify(int cur,int l,int r,int ql){ int now=++tot; sum[now]=sum[cur]+1;ch[now][0]=ch[cur][0];ch[now][1]=ch[cur][1]; if(l==r) return now; int mid=l+r>>1; if(ql<=mid) ch[now][0]=modify(ch[cur][0],l,mid,ql); else ch[now][1]=modify(ch[cur][1],mid+1,r,ql); return now; } void dfs(int now,int fa){ val[now]=std::lower_bound(g+1,g+1+len,val[now])-g; root[now]=modify(root[fa],1,len,val[now]); for(re int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(to==fa) continue; d[to]=d[now]+1;f[to][0]=now; for(int j=1;j<=18;j++) f[to][j]=f[f[to][j-1]][j-1]; // printf("now=%d,to=%d\n",now,to); // for(int j=0;j<=18;j++) printf("j=%d,f=%d\n",j,f[to][j]); dfs(to,now); } } inline int lca(int x,int y){ if(d[x]<d[y]) swap(x,y); // printf("x=%d,y=%d\n",x,y); for(re int i=18;~i;i--) if(d[f[x][i]]>=d[y]) x=f[x][i]; // printf("x=%d,y=%d\n",x,y); if(x==y) return x; for(re int i=18;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int cnts; int stkk[30][N*5]; int dp[N*5],dc; inline int newnode(){ return dc?dp[dc--]:++cnts; } int query(int pre,int pre2,int del,int l,int r,int ql,int qr,int pd){ // printf("l=%d,r=%d,ql=%d,qr=%d\n",l,r,ql,qr); if(ql<=l and r<=qr){ int now=0; for(re int i=1;i<=del+1;i++) now+=sum[stkk[pd][i]]; now=now-sum[pre]*del-sum[pre2]; // printf("now=%d\n",now); if(now>0) return 1; return 0; } int mid=l+r>>1; if(ql>mid){ for(re int i=1;i<=del+1;i++) stkk[pd][i]=ch[stkk[pd][i]][1]; return query(ch[pre][1],ch[pre2][1],del,mid+1,r,ql,qr,pd); } if(qr<=mid){ for(re int i=1;i<=del+1;i++) stkk[pd][i]=ch[stkk[pd][i]][0]; return query(ch[pre][0],ch[pre2][0],del,l,mid,ql,qr,pd); } int lef=newnode(); for(re int i=1;i<=del+1;i++) stkk[lef][i]=ch[stkk[pd][i]][0]; int kkk=query(ch[pre][0],ch[pre2][0],del,l,mid,ql,qr,lef); if(kkk){ dp[++dc]=lef; return 1; } for(re int i=1;i<=del+1;i++) stkk[lef][i]=ch[stkk[pd][i]][1]; kkk=query(ch[pre][1],ch[pre2][1],del,mid+1,r,ql,qr,lef); dp[++dc]=lef; return kkk; } void change(int &cur,int l,int r){ cur=++tot; if(l==r) return; int mid=l+r>>1; change(ch[cur][0],l,mid);change(ch[cur][1],mid+1,r); } inline int abs(int x){ if(x<0) return -x; return x; } int ques[N*5]; signed main(){ freopen("e.in","r",stdin);freopen("e.out","w",stdout); n=getint(),q=getint(),type=getint(); for(re int i=1;i<=n;i++) g[i]=val[i]=getint(); std::sort(g+1,g+1+n);len=std::unique(g+1,g+1+n)-g-1; for(re int i=1;i<n;i++){ int x=getint(),y=getint(); add(x,y);add(y,x); } change(root[0],1,len);d[1]=1;dfs(1,0); int lasans=0; while(q--){ int rr=getint(),k=getint(),now=(getint()-1+type*lasans)%n+1;ques[1]=root[now]; for(re int i=2;i<=k;i++){ int x=(getint()-1+type*lasans)%n+1; now=lca(now,x);ques[i]=root[x]; } if(k==1){ printf("%d\n",lasans=abs(g[val[now]]-rr)); continue; } int lala=newnode(); int p=std::lower_bound(g+1,g+1+len,rr)-g; int l=p,r=len,ans=0; while(l<=r){ // printf("l=%d,r=%d\n",l,r); for(re int i=1;i<=k;i++) stkk[lala][i]=ques[i]; int mid=l+r>>1; if(query(root[now],root[f[now][0]],k-1,1,len,p,mid,lala)) ans=mid,r=mid-1; else l=mid+1; } int ppp=0x3f3f3f3f; // printf("ans=%d\n",ans); if(ans) ppp=abs(g[ans]-rr); l=1,r=p-1,ans=0; while(l<=r){ // for(int i=0;i<k;i++) b[i]=a[i]; for(re int i=1;i<=k;i++) stkk[lala][i]=ques[i]; int mid=l+r>>1; if(query(root[now],root[f[now][0]],k-1,1,len,mid,p-1,lala)) ans=mid,l=mid+1; else r=mid-1; } if(ans) ppp=min(abs(g[ans]-rr),ppp); printf("%d\n",lasans=ppp);dp[++dc]=lala; } return 0; }
诶我发现我代码风格越来越难看了啊。。。
看来相比于强迫症,我还是更懒一些=。=
T3 可以先确定每一个二进制位对于答案逆序对的贡献,就是一段前缀相等然后这一位不相等的值,这个可以trie树预处理出来
然后就知道了[0,2^k)每个数的贡献 然而这样是O(2^k)的
考虑把一个二进制数拆成前一半和后一半
这样就显得很可做了 每一问二分一遍就好了
由于二分姿势错误调了好久 由于忘开long long调到怀疑人生
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; const int P=32; const int N=500005; const int M=N*30; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<ll,int> #define mp(A,B) std::make_pair(A,B) int n,k,p,tot=1; ll val[P][2],k1,k2; pii a[1<<16],b[1<<16]; int ch[M][2];ll sze[M]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } void insert(int x){ int now=1; for(int i=k-1;~i;i--){ if(x>>i&1) val[i][1]+=sze[ch[now][0]]; else val[i][0]+=sze[ch[now][1]]; if(!ch[now][x>>i&1]) ch[now][x>>i&1]=++tot; now=ch[now][x>>i&1]; sze[now]++; } } bool check(ll x){ int now=(1<<k2)-1;ll sum=0; for(int i=0;i<(1<<k1);i++){ while(now>=0 and a[i].first+b[now].first>x) now--; sum+=now+1; } return sum<p; } bool check2(ll x,ll y){ int now=(1<<k2)-1;ll sum=0; for(int i=0;i<(1<<k1);i++){ while(now>=0 and ((a[i].first+b[now].first>x) or ((a[i].first+b[now].first==x) and ((a[i].second|b[now].second)>y)))) now--; sum+=now+1; } return sum<p; } signed main(){ n=getint(),k=getint(),p=getint(); for(int i=1;i<=n;i++){ int x=getint(); insert(x); } k1=k>>1,k2=k-k1; for(int i=0;i<(1<<k1);i++){ for(int j=0;j<k1;j++){ if(i>>j&1) a[i].first+=val[j+k2][1]; else a[i].first+=val[j+k2][0]; } a[i].second=i<<k2; } std::sort(a,a+(1<<k1)); for(int i=0;i<(1<<k2);i++){ for(int j=0;j<k2;j++){ if(i>>j&1) b[i].first+=val[j][1]; else b[i].first+=val[j][0]; } b[i].second=i; } std::sort(b,b+(1<<k2)); // for(int i=0;i<(1<<k1);i++) printf("i=%d,a=%d,b=%d\n",i,a[i].first,a[i].second); // for(int i=0;i<(1<<k2);i++) printf("i=%d,a=%d,b=%d\n",i,b[i].first,b[i].second); ll l=0,r=(ll)n*(n-1)/2,ans=-1; while(l<=r){ ll mid=l+r>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%lld ",ans+1); l=0,r=(1<<k)-1;ll ans2=-1; while(l<=r){ ll mid=l+r>>1; if(check2(ans+1,mid)) ans2=mid,l=mid+1; else r=mid-1; } printf("%lld",ans2+1); return 0; }
2018.10.13
自己菜没啥好说的
T1 前缀后缀搞一下就行了
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; const int N=100005; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int n,x; ll val[N],qzh[N],hzh[N]; ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ freopen("poker.in","r",stdin);freopen("poker.out","w",stdout); n=getint(),x=getint(); for(int i=1;i<=n;i++) val[i]=getint(),qzh[i]=qzh[i-1]|val[i]; std::sort(val+1,val+1+n); for(int i=n;i;i--) hzh[i]=hzh[i+1]|val[i]; ll ans=0; for(int i=1;i<=n;i++) ans=max(ans,(val[i]*x)|qzh[i-1]|hzh[i+1]); printf("%lld\n",ans); return 0; }
T2 一个显然的容斥想法是像幸运数字那样做一波,复杂度不会算,但是觉得没有2^50那么多,因为1e9以内一个数最多只有9个不同的质因子,感觉加上这个剪枝可以随便过。事实是确实可以随便过,但是考场上不知道怎么想的觉得这个 gcd(val[i],n)!=1 等价于 val[i] 是 n 的约数,然后就凉凉。。。 加上一句 val[i]=gcd(val[i],n) 就能 A 了
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) ll n,m; int cnts; ll val[55],ans,b[55]; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } void dfs(int now,int used,ll lcm){ if(lcm>n) return; if(now>cnts){ if(used&1) ans+=(n-1)/lcm; else if(used) ans-=(n-1)/lcm; return; } dfs(now+1,used+1,lcm*b[now]/gcd(lcm,b[now])); dfs(now+1,used,lcm); } ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ freopen("running.in","r",stdin);freopen("running.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=m;i++){ val[i]=getint(); if(gcd(val[i],n)==1) return printf("0"),0; val[i]=gcd(val[i],n); } std::sort(val+1,val+1+m);m=std::unique(val+1,val+1+m)-val-1; for(int i=1;i<=m;i++){ int flag=0; for(int j=1;j<i;j++){ if(val[i]%val[j]==0) flag=1; } if(flag)continue; b[++cnts]=val[i]; } std::reverse(b+1,b+1+cnts); dfs(1,0,1); printf("%lld\n",n-ans-1); return 0; }
然而正解不是这样的 白瞎了我证明半天复杂度可以艹过去
正解是对于 n 的每个约数 d ,如果有某个 i 满足 gcd(val_i,n)|d,那么所有 gcd(x,n)=d 的 x 都是可以被经过的,答案加上 phi(n/d) 就好了
woc 感觉这样好玄学而且好难想
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int N=55; using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) ll n,m; vector<ll> v; ll val[N],ans; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } ll phi(ll x){ ll now=x,sq=sqrt(x); for(int i=2;i<=sq;i++){ if(x%i==0){ now=now/i*(i-1); while(x%i==0) x/=i; } } if(x>1) now=now/x*(x-1);return now; } signed main(){ n=getint(),m=getint(); for(int i=1;i<=m;i++){ val[i]=getint(); ll g=gcd(val[i],n); if(g==1) return printf("0"),0; val[i]=g; } ll sq=sqrt(n); for(ll i=1;i<=sq;i++){ if(n%i==0){ v.pb(i); if(i*i!=n) v.pb(n/i); } } for(int i=0;i<v.size();i++){ for(int j=1;j<=m;j++){ if(v[i]%val[j]==0){ ans+=phi(n/v[i]); break; } } } printf("%lld\n",n-ans); return 0; }
有没有老哥资助个键盘啊
noip考完就还 考退役还 考AK也还
2018.10.15
考完看确实三道傻逼题啊... 然而考场上没想出来T1傻逼递推也没推出来T3柿子 然而还是rank3?
T1 哇这递推还想不出来noip还咋考啊 显然有递推性质啊不知道考场上怎么想的
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; const int mod=99824353; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int val[1<<25]; int n,k,Seed,ans; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ n=getint(),k=getint(),Seed=getint(); int maxn=1<<k; for(int i=1;i<maxn;i++) val[i]=(val[i>>1]>>1)|((i&1)<<k-1); for(int i=1;i<=n;i++){ Seed=(214013LL*Seed+2531011)&((1<<k)-1); ans=((ll)ans*233+val[Seed])%mod; } printf("%d\n",ans); return 0; }
T2 哈希随便做做就行了
T3 据说一堆打表怪??可以把式子推成 C(n,i)*i^2 的结构,然后ztb讲的组合意义比较好理解,每一项就相当于求包含 i 个元素的集合平方之后子集的个数 然后考虑贡献就做完了 %ztb
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; const int mod=1e9+7; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int ksm(int a,int b){ int ans=1; while(b){ if(b&1)ans=(ll)ans*a%mod; a=(ll)a*a%mod;b>>=1; } return ans; } int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ int n=getint(); printf("%lld\n",((ll)ksm(2,n-1)*n%mod+(ll)ksm(2,n-2)*n%mod*(n-1)%mod)%mod); return 0; }
2018.10.16
考回原形mmp
今天好不在状态啊没能秒的题就不想继续思考了秒了的题也写挂了啊啊啊
其实T1不挂的话其他两个就算想不出来也能rank3啊
T1 傻逼题 注意点以后双模数哈希别tm再打错变量了直接崩了100pts
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; const int N=1000005; const int base1=29; const int base2=31; const int mod1=1e9+7; const int mod2=1e9+9; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) char ch[N]; int len[2][N]; int qz[2],hz[2]; int qzhsh[2][N],n; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } bool check(int l1,int r1,int l2,int r2){ int ans=0; if(r1>=l1 and l2<=r2){ int a=((ll)qzhsh[0][r2]-(ll)qzhsh[0][l2-1]*len[0][r2-l2+1]%mod1+mod1)%mod1; int b=(ll)qzhsh[0][r1]*len[0][r2-l2+1]%mod1; if(((ll)a+b)%mod1!=hz[0]) return 0; a=((ll)qzhsh[1][r2]-(ll)qzhsh[1][l2-1]*len[1][r2-l2+1]%mod2+mod2)%mod2; b=((ll)qzhsh[1][r1]*len[1][r2-l2+1]%mod2); return ((ll)a+b)%mod2==hz[1]; } else if(l1>r1){ ans=((ll)qzhsh[0][r2]-(ll)qzhsh[0][l2-1]*len[0][r2-l2+1]%mod1+mod1)%mod1; if(ans!=hz[0]) return 0; ans=((ll)qzhsh[1][r2]-(ll)qzhsh[1][l2-1]*len[1][r2-l2+1]%mod2+mod2)%mod2; return ans==hz[1]; } else{ return qzhsh[0][r1]==hz[0] and qzhsh[1][r1]==hz[1]; } } bool check2(int l1,int r1,int l2,int r2){ // printf("A=%d,b=%d,c=%d,d=%d\n",l1,r1,l2,r2); if(l2<=r2){ int a=((ll)qzhsh[0][r2]-(ll)qzhsh[0][l2-1]*len[0][r2-l2+1]%mod1+mod1)%mod1; int b=((ll)qzhsh[0][r1]-(ll)qzhsh[0][l1-1]*len[0][r1-l1+1]%mod1+mod1)%mod1; // printf("a=%d,b=%d\n",a,b); b=((ll)b*len[0][r2-l2+1]%mod1); a=((ll)a+b)%mod1; // printf("a=%d,qz=%d\n",a,qz[0]); if(a!=qz[0]) return 0; a=((ll)qzhsh[1][r2]-(ll)qzhsh[1][l2-1]*len[1][r2-l2+1]%mod2+mod2)%mod2; b=((ll)qzhsh[1][r1]-(ll)qzhsh[1][l1-1]*len[1][r1-l1+1]%mod2+mod2)%mod2; b=((ll)b*len[1][r2-l2+1]%mod2); a=((ll)a+b)%mod2; return a==qz[1]; } else{ int a=((ll)qzhsh[0][r1]-(ll)qzhsh[0][l1-1]*len[0][r1-l1+1]%mod1+mod1)%mod1; if(a!=qz[0]) return 0; a=((ll)qzhsh[1][r1]-(ll)qzhsh[1][l1-1]*len[1][r1-l1+1]%mod2+mod2)%mod2; return a==qz[1]; } } signed main(){ // freopen("lgg.in","r",stdin); // freopen("lgg.out","w",stdout); len[0][0]=len[1][0]=1; for(int i=1;i<=N-5;i++){ len[0][i]=(ll)len[0][i-1]*base1%mod1; len[1][i]=(ll)len[1][i-1]*base2%mod2; } int T=getint(); while(T--){ n=getint();scanf("%s",ch+1); if(n%2==0){printf("NOT POSSIBLE\n");continue;} for(int i=1;i<=n;i++){ qzhsh[0][i]=((ll)qzhsh[0][i-1]*base1%mod1+ch[i]-'A'+1)%mod1; qzhsh[1][i]=((ll)qzhsh[1][i-1]*base2%mod2+ch[i]-'A'+1)%mod2; } int flag=0,p=n>>1,idx=0,aaa=0; qz[0]=qzhsh[0][p];qz[1]=qzhsh[1][p]; hz[0]=((ll)qzhsh[0][n]-(ll)qzhsh[0][n-p]*len[0][p]%mod1+mod1)%mod1; hz[1]=((ll)qzhsh[1][n]-(ll)qzhsh[1][n-p]*len[1][p]%mod2+mod2)%mod2; for(int i=1;i<=n;i++){ if(i<=p+1){ if(check(1,i-1,i+1,p+1)){ if(idx!=hz[0]) flag++,idx=hz[0],aaa=i; } } else{ if(check2(p+1,i-1,i+1,n)){ if(idx!=qz[0]) flag++,idx=qz[0],aaa=i; } } } if(flag>1) printf("NOT UNIQUE\n"); else if(!flag) printf("NOT POSSIBLE\n"); else { int cnt=0; for(int i=1;cnt<p and i<=n;i++) if(i!=aaa) putchar(ch[i]),cnt++; puts(""); } } return 0; }
T2 这个A+B看上去很诡异... 分析一下可以发现实际上就是某个位置既放了A又放了B
这就等价于放小于等于N个A和小于等于N个B且总和=K的方案数 可以枚举A放了i个,算出B放了j个,然后答案加上C(N,i)*C(N,j)就行了
一堆沙雕特判出题人真是有毒
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; const int N=10000005; const int mod=998244353; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int n;ll a,b,k; int fac[N],ifac[N]; int ksm(int a,int b){ int ans=1; while(b){ if(b&1) ans=(ll)ans*a%mod; a=(ll)a*a%mod;b>>=1; } return ans; } ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } int C(int n,int m){ return (ll)fac[n]*ifac[m]%mod*ifac[n-m]%mod; } signed main(){ n=getint(),a=getint(),b=getint(),k=getint(); fac[0]=ifac[0]=1; for(int i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%mod; ifac[n]=ksm(fac[n],mod-2); for(int i=n-1;i;i--) ifac[i]=(ll)ifac[i+1]*(i+1)%mod; int ans=0; if(!b){ if(!a){ if(k) return printf("0"),0; return printf("%d\n",ksm(2,2*n)),0; } ll q=k/a; if(k%a) return printf("0"),0; if(q>n) return printf("0"),0; for(int i=0;i<=n;i++) ans=((ll)ans+(ll)C(n,q)*C(n,i)%mod)%mod; return printf("%d",ans),0; } for(int i=0;i<=n;i++){ if((k-a*i)%b) continue; if(k-a*i<0) break; ll j=(k-a*i)/b; if(j>n) continue; ans=((ll)ans+(ll)C(n,i)*C(n,j)%mod)%mod; } printf("%d\n",ans); return 0; }
T3 看到点数这么小直接想floyd+矩乘啊 降智越来越严重了啊啊啊
直接动态dp就行了
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int M=2e3+5; const int N=1e5+5; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) #define ls cur<<1 #define rs cur<<1|1 #define lss ls,l,mid,ql,qr #define rss rs,mid+1,r,ql,qr pii qj[10005]; int dis[10][10]; int n,sbctr,cnts[2005]; struct Mat{ int a[10][10]; Mat(){ memset(a,0x3f,sizeof a); for(int i=0;i<9;i++) a[i][i]=0; } friend Mat operator*(Mat x,Mat y){ Mat z; for(int i=0;i<9;i++) for(int k=0;k<9;k++) for(int j=0;j<9;j++) z.a[i][j]=min(z.a[i][j],min(x.a[i][k]+y.a[k][j],min(x.a[i][j],y.a[i][j]))); return z; } }sum[M<<2],val[M]; struct Edge{ int x,y,prio,dis; friend bool operator<(Edge a,Edge b){ return a.prio<b.prio; } }edge[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } void modify(int cur,int l,int r,int ql,int qr,Mat c){ if(l==r){sum[cur]=c;return;} int mid=l+r>>1; ql<=mid?modify(lss,c):modify(rss,c); sum[cur]=sum[ls]*sum[rs]; } Mat query(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return sum[cur]; int mid=l+r>>1; if(qr<=mid) return query(lss); if(mid<ql) return query(rss); return query(lss)*query(rss); } void build(int cur,int l,int r){ if(l==r){ sum[cur]=val[l]; return; } int mid=l+r>>1; build(ls,l,mid);build(rs,mid+1,r); sum[cur]=sum[ls]*sum[rs]; } signed main(){ n=getint(),sbctr=getint(); for(int i=1;i<=n;i++){ edge[i].prio=getint(),edge[i].x=getint(),edge[i].y=getint(),edge[i].dis=getint(); val[edge[i].prio].a[edge[i].x][edge[i].y]=min(val[edge[i].prio].a[edge[i].x][edge[i].y],edge[i].dis); } for(int i=1;i<=2000;i++) for(int k=0;k<9;k++) for(int a=0;a<9;a++) for(int b=0;b<9;b++) val[i].a[a][b]=min(val[i].a[a][b],val[i].a[a][k]+val[i].a[k][b]); build(1,1,2000); int T=getint(); while(T--){ int s=getint(),t=getint(),tot=getint(); Mat z; for(int i=0;i<9;i++) z.a[i][i]=0; for(int i=1;i<=tot;i++) qj[i].first=getint(),qj[i].second=getint(); std::sort(qj+1,qj+1+tot); for(int i=1;i<=tot;i++) z=z*query(1,1,2000,qj[i].first,qj[i].second); printf("%d\n",z.a[s][t]>=0x3f3f3f3f?-1:z.a[s][t]); } return 0; }
2018.10.27
今天的题好劲啊!
md文件打错了掉了十几名fuck
T1 CDQ板子题多个0??
哈哈哈哈哈哈哈zyz写树套树就12分哈哈哈哈哈哈哈
考虑计算这三个东西 x=[ai<aj][bi<bj],y=[bi<bj][ci<cj],z=[ci<cj][ai<aj]
观察到每个无序对(i,j)都被计算了至少一遍。注意是无序对,如果有序对(i,j)满足0个或1个条件的话,(j,i)就一定满足3个或2个了。在上面算出来的xyz中,满足3个的无序对算了3次,满足2个的无序对算了1次,我们的目的是求出满足3个的个数。显然用x+y+z减去C(n,2)再除以2就好了。
mb洛谷瞧不起我不给我分配好评测机导致现在还T着不管了
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int N=2e6+6; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) unsigned int SA,SB,SC; int n,a[N],b[N],c[N],f[N],pos[N]; void add(int x){ while(x<=n) f[x]++,x+=x&-x; } int query(int x){ int now=0; while(x) now+=f[x],x-=x&-x; return now; } unsigned int rd(){ SA^=SA<<16;SA^=SA>>5;SA^=SA<<1; unsigned int t=SA;SA=SB;SB=SC;SC^=t^SA;return SC; } void gen(int *P){ for (int i=1;i<=n;++i) P[i]=i; for (int i=1;i<=n;++i) swap(P[i],P[1+rd()%n]); } ll sol(int *a,int *b){ for(int i=1;i<=n;i++) pos[a[i]]=i; ll now=0; for(int i=1;i<=n;i++) now+=query(b[pos[i]]),add(b[pos[i]]); for(int i=1;i<=n;i++) f[i]=0; return now; } signed main(){ freopen("cdq.in","r",stdin);freopen("cdq.out","w",stdout); scanf("%d%u%u%u",&n,&SA,&SB,&SC); gen(a);gen(b);gen(c); ll ans=sol(a,b)+sol(b,c)+sol(c,a); printf("%lld\n",(ans-(ll)n*(n-1)/2)/2); return 0; }
T2 吼题啊吼题
其实考场上立马想到了状压质因子,但是之前一直在想T1导致时间不是很够做这题的时候就非常慌张,连一个2e8以内的约数个数表都没想着打,就以为至少得有5000个了,然后算了一下空间开不下瞬间心态爆炸放弃写正解了。其实i207M这个博客写的挺清楚的,1e8以内最多只有800个因数,这就很可做了。
然后考场上还有一个一直纠结的地方是不会求强制不包含某个元素的方案数。想到了前缀后缀合并但是感觉很不优美,感觉这个东西可以像消失之物那道题一样倒着推回去,但是死活过不了样例不知道哪里错了最后放弃梦想敲了55分暴力。
正解实际上就是前后缀合并,但是暴力合并一次是O(4^16)的,考虑降低复杂度。
先把暴力合并的式子写下来长这样:f[a|b][c|d]+=x[a][c]*y[b][d] 这是一个集合并的卷积,可以用莫比乌斯变换实际上就是高维前缀和快速算出来,复杂度能降低成O(16*2^16) 。一会应该会新开个博文写一下这个东西
但是求出来的并不是我们想求的而是一个子集和,对于每个数我们还需要减去它的子集和。这里如果用O(3^16)暴力减的话就很不优美了,我们还是可以通过高维差分再把这个子集和减回去,复杂度仍然是O(16*2^16)的,做到这样的复杂度就很优秀了。 总复杂度O(800*16*2^16)。
#pragma GCC optimize(2) #include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int K=8; const int N=1005; typedef double db; typedef long long ll; const int mod=1e9+7; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) int n,A,B,q; std::map<int,int> fs; int pc,p[K],cnts[2][K]; int val[2][N],tot,ans[N],g[1<<K][1<<K]; int f[1<<K][1<<K],qz[N][1<<K][1<<K],hz[N][1<<K][1<<K]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ n=getint(),A=getint(),B=getint(),q=getint(); if(B%A){ puts("0"); while(q--) puts("0"); return 0; } int sq=sqrt(B),x=B; for(int i=2;i<=sq;i++){ if(x%i==0){ p[++pc]=i; while(x%i==0) x/=i,cnts[0][pc]++; } } if(x>1) p[++pc]=x,cnts[0][pc]=1; x=A; for(int i=1;i<=pc;i++){ while(x%p[i]==0) x/=p[i],cnts[1][i]++; } for(int i=1;i<=sq and i<=n;i++){ if(B%i==0 and i%A==0){ tot++;fs[i]=tot;x=i; for(int j=1;j<=pc;j++){ int now=0; while(x%p[j]==0) now++,x/=p[j]; if(now==cnts[0][j]) val[0][tot]|=1<<j-1; if(now==cnts[1][j]) val[1][tot]|=1<<j-1; } } int pp=B/i; if(i*i==B) continue; if(pp>n) continue; if(B%pp==0 and pp%A==0){ tot++;fs[pp]=tot;x=pp; for(int j=1;j<=pc;j++){ int now=0; while(x%p[j]==0) now++,x/=p[j]; if(now==cnts[0][j]) val[0][tot]|=1<<j-1; if(now==cnts[1][j]) val[1][tot]|=1<<j-1; } } } int maxn=1<<pc; qz[0][0][0]=1; for(int i=1;i<=tot;i++){ memcpy(qz[i],qz[i-1],sizeof qz[i-1]); for(int j=maxn-1;~j;j--) for(int p=maxn-1;~p;p--) (qz[i][j|val[0][i]][p|val[1][i]]+=qz[i][j][p])%=mod; } hz[tot+1][0][0]=1; for(int i=tot;i;i--){ memcpy(hz[i],hz[i+1],sizeof hz[i+1]); for(int j=maxn-1;~j;j--) for(int p=maxn-1;~p;p--) (hz[i][j|val[0][i]][p|val[1][i]]+=hz[i][j][p])%=mod; } int las=qz[tot][maxn-1][maxn-1]; memset(ans,-1,sizeof ans); while(q--){ int x=getint(); if(x%A or B%x){ puts("0"); continue; } int idx=fs[x]; if(!val[0][idx] and !val[1][idx]){ printf("%d\n",las); continue; } if(~ans[idx]){ printf("%d\n",ans[idx]); continue; } for(int p=0;p<pc;p++) for(int i=0;i<maxn;i++) if(i>>p&1){ for(int j=0;j<maxn;j++) (qz[idx-1][i][j]+=qz[idx-1][i^(1<<p)][j])%=mod, (hz[idx+1][i][j]+=hz[idx+1][i^(1<<p)][j])%=mod; } for(int p=0;p<pc;p++) for(int j=0;j<maxn;j++) if(j>>p&1){ for(int i=0;i<maxn;i++) (qz[idx-1][i][j]+=qz[idx-1][i][j^(1<<p)])%=mod, (hz[idx+1][i][j]+=hz[idx+1][i][j^(1<<p)])%=mod; } for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) g[i][j]=(ll)qz[idx-1][i][j]*hz[idx+1][i][j]%mod; for(int p=0;p<pc;p++) for(int i=0;i<maxn;i++) if(i>>p&1) for(int j=0;j<maxn;j++) g[i][j]=(g[i][j]-g[i^(1<<p)][j]+mod)%mod; for(int p=0;p<pc;p++) for(int j=0;j<maxn;j++) if(j>>p&1) for(int i=0;i<maxn;i++) g[i][j]=(g[i][j]-g[i][j^(1<<p)]+mod)%mod; printf("%d\n",ans[idx]=(las-g[maxn-1][maxn-1]+mod)%mod); } return 0; }
10.29-10.30
找状态
Day1
T1 数据范围显然O(n^2+q)的复杂度然后差分一下就没了
T2 可以暴力记搜 考场上自以为想到了它的最优策略结果想了个错的 其实记搜的时候前后取一个max就行了也不用想太多 该暴力的时候还是应该暴力啊
T3 比较麻烦的树形DP
Day2
T1 有相同质因子的数连边并查集就行了
T2 可以折半做 其实这种数据范围比较暧昧的比如d<=30甚至d<=20除了状压都可以想到折半做 然后bitset优化一下就行了 然后学习了一下bitset怎么优化这玩意儿 思路要反着想挺烦人的
#include<set> #include<map> #include<cmath> #include<queue> #include<bitset> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::bitset; using std::vector; const int N=91; const int maxn=1<<11; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) int n,m,d,cnts[1<<21]; bitset<N> g0[N],g1[N],f[11][maxn+5],g[11][N][maxn+5]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ freopen("y.in","r",stdin);freopen("y.out","w",stdout); n=getint(),m=getint(),d=getint(); int k1=d>>1,k2=d-k1; for(int i=1;i<=m;i++){ int x=getint(),y=getint(),z=getint(); if(z) g1[x][y]=g1[y][x]=1; else g0[x][y]=g0[y][x]=1; } f[0][0][1]=1; for(int dep=1;dep<=k1;dep++){ for(int i=0;i<1<<k1;i++){ for(int j=1;j<=n;j++){ if(f[dep-1][i][j]){ f[dep][i<<1|1]|=g1[j]; f[dep][i<<1]|=g0[j]; } } } } for(int i=1;i<=n;i++) g[0][i][0][i]=1; for(int dep=1;dep<=k2;dep++){ for(int i=0;i<1<<k2;i++){ for(int j=1;j<=n;j++){ for(int p=1;p<=n;p++){ if(g[dep-1][j][i][p]){ g[dep][j][i<<1|1]|=g1[p]; g[dep][j][i<<1]|=g0[p]; } } } } } for(int i=0;i<1<<k1;i++){ if(f[k1][i].any()){ for(int j=1;j<=n;j++){ if(f[k1][i][j]){ for(int p=0;p<1<<k2;p++){ if(g[k2][j][p].any()) cnts[i<<k2|p]=1; } } } } } int ans=0; for(int i=0;i<1<<d;i++) if(cnts[i]) ans++; printf("%d\n",ans); return 0; }
T3 咕咕咕着先
总结一下吧这两天考的都这么差
其实最近心太浮躁了,还有就是每天切了傻逼T1之后就有点想放弃思考了 自己做出来的别人肯定也能做出来所以应该更努力的去想T2T3正解才对
还有就是暴力怎么打都打不对了 这是最严重的问题 每天即使把能打的暴力分写对甚至把写了但写挂的暴力分写对也能排大概rank3,4这样 所以暴力写对也是很重要的一个东西
再有就是时间分配可能是个问题 大概安排一下以后考试(是那种noip级别的考试)的时间吧:5min先读完3道题 30min争取切掉T1并过拍 30min写好T2T3暴力 然后抉择T2和T3去想哪个 花上1~2h去想去写这道题尽最大可能做出来 剩下30min想另一道题高分暴力 但主要还是检查空间 检查文件
嗯下次考试先用这个策略试一试
最主要的还是暴力要写对
还有10天,不努力就真晚了
悔不当初