标签:
感觉CQOI的难度挺好的,比较贴近自身,所以拿出来做了一下
CQOI2016 Day1 T1:不同的最小割
涉及算法:最小割/分治/最小割树
思路:
最小割树裸题,直接分治最小割,记录下答案,最后排序一下,统计不同的答案即可
CODE:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } #define maxn 1000 #define maxm 100010 int n,m,q,t,ans[maxn],tot,id[maxn],tmp[maxn]; struct Edgenode{int next,to,cap;}edge[maxm]; int head[maxn],cnt=1; void add(int u,int v,int w) {cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].cap=w;} void insert(int u,int v,int w) {add(u,v,w); add(v,u,w);} int dis[maxn],que[maxn<<1],cur[maxn],S,T; bool bfs() { memset(dis,-1,sizeof(dis)); que[0]=S; dis[S]=0; int he=0,ta=1; while (he<ta) { int now=que[he++]; for (int i=head[now]; i; i=edge[i].next) if (edge[i].cap && dis[edge[i].to]==-1) dis[edge[i].to]=dis[now]+1,que[ta++]=edge[i].to; } return dis[T]!=-1; } int dfs(int loc,int low) { if (loc==T) return low; int w,used=0; for (int i=cur[loc]; i; i=edge[i].next) if (edge[i].cap && dis[edge[i].to]==dis[loc]+1) { w=dfs(edge[i].to,min(low-used,edge[i].cap)); edge[i].cap-=w; edge[i^1].cap+=w; used+=w; if (edge[i].cap) cur[loc]=i; if (used==low) return low; } if (!used) dis[loc]=-1; return used; } #define inf 0x7fffffff int dinic() { int tmp=0; while (bfs()) { for (int i=1; i<=n; i++) cur[i]=head[i]; tmp+=dfs(S,inf); } return tmp; } void init() { cnt=1; memset(ans,0,sizeof(ans)); memset(head,0,sizeof(head)); } bool visit[maxn]; void DFS(int x) { visit[x]=1; for (int i=head[x]; i; i=edge[i].next) if (edge[i].cap && !visit[edge[i].to]) DFS(edge[i].to); } void work(int L,int R) { if (L==R) return; for (int i=2; i<=cnt; i+=2) edge[i].cap=edge[i^1].cap=(edge[i].cap+edge[i^1].cap)>>1; S=id[L],T=id[R]; int maxflow=dinic(); memset(visit,0,sizeof(visit)); DFS(S); // for (int i=1; i<=n; i++) if (visit[i]) // for (int j=1; j<=n; j++) if (!visit[j]) // ans[i][j]=ans[j][i]=min(ans[i][j],maxflow); ans[++tot]=maxflow; int l=L,r=R; for (int i=L; i<=R; i++) if (visit[id[i]]) tmp[l++]=id[i]; else tmp[r--]=id[i]; for (int i=L; i<=R; i++) id[i]=tmp[i]; work(L,l-1); work(r+1,R); } int main() { // freopen("mincuto.in","r",stdin); // freopen("mincuto.out","w",stdout); init(); n=read(),m=read(); for (int i=1; i<=n; i++) id[i]=i; for (int u,v,w,i=1; i<=m; i++) u=read(),v=read(),w=read(),insert(u,v,w); work(1,n); sort(ans+1,ans+tot+1); int an=1; for (int i=2; i<=tot; i++) if (ans[i]!=ans[i-1]) an++; printf("%d\n",an); return 0; }
CQOI2016 Day1 T2:K远点对
涉及算法:凸包/KD-Tree/可并堆
思路:
先把所有点都放到kd树里去,维护一个小根堆,枚举每个点。每次在kd树种查询的时候,就相当于当前的最优解是堆顶,像查最远点对一样在kd树里查就行了。
CODE:
暂时不能实现,等学习KD-Tree后填坑
CQOI2016 Day1 T3:手机号码
涉及算法:数位DP/记忆化搜索
思路:
涉及F[i][j][0/1][0/1][0/1][0/1][0/1]表示位数为i,最高位为j,最高位连续两个是否是相同的,是否有连续3个相同的,是否有4,是否有8,前缀和原数前缀的大小关系
枚举k1,k2,k3,k4,k5转移统计答案即可,注意一些细节(记忆化搜索应该比较好实现)
CODE:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; long long F[20][15][2][2][2][2][2],l,r; int p[20],num; long long cal(long long x) { memset(F,0,sizeof(F)); long long ans=0; int len=0,digit[12],a,b,c,d,e; while(x){digit[++len]=x%10; x/=10;} reverse(digit+1,digit+len+1); F[0][10][0][0][0][0][1]=1; for (int i=0; i<=len-1; i++) for (int j=0; j<=10; j++) for (int k1=0; k1<=1; k1++) for (int k2=0; k2<=1; k2++) for (int k3=0; k3<=1; k3++) for (int k4=0; k4<=1; k4++) for (int k5=0; k5<=1; k5++) if (F[i][j][k1][k2][k3][k4]) for (int k=0; k<=9; k++) { if (k5 && (k>digit[i+1])) continue; if (k==j) a=1; else a=0; if (k2==0) b=(k1+a)==2; else b=k2; if (k3==0) c=(k==4); else c=k3; if (k4==0) d=(k==8); else d=k4; if ((c+d)==2) continue; if (k5 && (k==digit[i+1])) e=1; else e=0; F[i+1][k][a][b][c][d][e]+=F[i][j][k1][k2][k3][k4][k5]; } for (int i=0; i<=9; i++) for (int k1=0; k1<=1; k1++) for (int k3=0; k3<=1; k3++) for (int k4=0; (k4<=1)&&(k4+k3<2); k4++) ans+=F[len][i][k1][1][k3][k4][0]; return ans; } int main() { scanf("%lld%lld",&l,&r); printf("%lld\n",cal(r+1)-cal(l)); return 0; }
CQOI2016 Day2 T1:密钥破解
涉及算法:数论/Pollard_Rho分解/ExGcd/乘法逆元/快速幂/快速乘/模拟
思路:
用Pollard_Rho分解N,得到P,Q,同时计算出r;剩下的根据题意模拟
利用ExGcd求解e在r意义下的逆元,最后答案用快速幂算出即可
CODE:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<cstdlib> using namespace std; long long read() { long long x=0,f=1; char ch=getchar(); while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();} while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();} return x*f; } long long e,N,c,r,P,Q; long long Quick_Mul(long long x,long long y,long long p) { long long re=0; for (long long i=y; i; i>>=1,x=(x+x)%p) if (i&1) re=(re+x)%p; return re; } long long Quick_Pow(long long x,long long y,long long p) { long long re=1; for (long long i=y; i; i>>=1,x=Quick_Mul(x,x,p)) if (i&1) re=Quick_Mul(re,x,p); return re; } void Exgcd(long long a,long long b,long long &x,long long &y) { if (b==0) {x=1; y=0; return;} Exgcd(b,a%b,y,x); y-=(a/b)*x; } long long GetInv(long long n,long long p) { long long x,y; Exgcd(n,p,x,y); return (x%p+p)%p; } long long Gcd(long long a,long long b) { if (b==0) return a; return Gcd(b,a%b); } #define T 10007 long long Pollard_Rho(long long n) { long long x,y,cnt=1,k=2; x=rand()%(n-1)+1; y=x; while (1) { cnt++; x=(Quick_Mul(x,x,n)+T)%n; long long gcd=Gcd(abs(x-y),n); if (1<gcd && gcd<n) return gcd; if (x==y) return n; if (cnt==k) y=x,k<<=1; } } int main() { srand(T); e=read(),N=read(),c=read(); P=Pollard_Rho(N); Q=N/P; r=(P-1)*(Q-1); long long Inv=GetInv(e,r); printf("%lld %lld",Inv,Quick_Pow(c,Inv,N)); return 0; }
CQOI2016 Day2 T2:路由表
涉及算法:Trie树/单调栈/进制转化
思路:
A/Q操作都对IP进行二进制转化后进行处理
A操作将转化后的IP前掩码为加到Trie树中,结尾打上时间戳标记
Q操作,二进制在Trie树上跑跑,将答案记录下来,排序后统计一下答案即可,或者利用单调栈维护即可
CODE:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int m,Ip[50]; #define maxn 1000100 int ch[maxn][2],pos[maxn],rt=1,cnt=1,sz=1,ti=0; void Insert(int *ip,int l,int x) { int now=rt; for (int i=1; i<=l; i++) { if (!ch[now][ip[i]]) ch[now][ip[i]]=++sz; now=ch[now][ip[i]]; } pos[now]=x; } struct Node { int a,b; bool operator < (const Node & A) const {return a<A.a;} }; Node stack[maxn]; int top; int Query(int *ip,int L,int R) { int now=rt,re=0,m=-1; top=0; for (int i=1; i<=32; i++) { if (!ch[now][ip[i]]) break; now=ch[now][ip[i]]; if (pos[now] && pos[now]<=R) stack[++top]=Node{pos[now],i+1}; } sort(stack+1,stack+top+1); for (int i=1; i<=top; i++) { Node now=stack[i]; if (m<now.b) {m=now.b; if (now.a>=L) re++;} } return re; } int main() { scanf("%d",&m); for (int i=1; i<=m; i++) { char opt[5]; scanf("%s",opt); if (opt[0]==‘A‘) { ti++; int ip,len=0,l; memset(Ip,0,sizeof(Ip)); for (int j=1; j<=3; j++) { scanf("%d.",&ip); for (int k=7; k>=0; k--) Ip[++len]=(1&(ip>>k)); } scanf("%d/",&ip); for (int k=7; k>=0; k--) Ip[++len]=(1&(ip>>k)); scanf("%d",&l); //for (int j=1; j<=len; j++) printf("%d",Ip[j]); puts(""); Insert(Ip,l,ti); } if (opt[0]==‘Q‘) { int ip,len=0,l,r; memset(Ip,0,sizeof(Ip)); for (int j=1; j<=3; j++) { scanf("%d.",&ip); for (int k=7; k>=0; k--) Ip[++len]=(1&(ip>>k)); } scanf("%d",&ip); for (int k=7; k>=0; k--) Ip[++len]=(1&(ip>>k)); scanf("%d %d",&l,&r); //for (int j=1; j<=len; j++) printf("%d",Ip[j]); puts(""); printf("%d\n",Query(Ip,l,r)); } } return 0; }
CQOI2016 Day2 T3:伪光滑数
涉及算法:可持久化可并堆/DP/堆/贪心/暴力
思路:
预处理出$<128$的全部质数,那么很显然,可以对数进行拆分了.
考虑题目中所说的, 所以不妨枚举倍数,对于$prime[i]^{j}$,扔进堆中
然后从队首取K次即可,对于每次取出的数,除以它的最大质因子,乘上比他最大质因子小的最大的质数,再扔回堆中
可持久化可并堆+DP的方法并不会....(留坑以后来看看)
CODE:
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> using namespace std; struct Node { long long data; int zs,nt,mp; bool operator < (const Node & A) const {return data<A.data;} }now,tmp; priority_queue <Node> q; long long n,x; int k,j; int prime[50]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127},cnt=31; int main() { scanf("%lld%d",&n,&k); for (int i=1; i<=cnt; i++) for (x=j=1; ; j++) { x*=(long long)prime[i]; if (x>n) break; tmp.data=x,tmp.zs=j,tmp.nt=i-1,tmp.mp=i; //printf("%lld %d %d %d\n",tmp.data,tmp.zs,tmp.nt,tmp.mp); q.push(tmp); } while (k--) { now=q.top(); q.pop(); if (now.zs>1) for (int i=now.nt; i; i--) { tmp.data=(long long)now.data/prime[now.mp]*prime[i]; tmp.zs=now.zs-1; tmp.nt=i; tmp.mp=now.mp; //printf("%lld %d %d %d\n",tmp.data,tmp.zs,tmp.nt,tmp.mp); q.push(tmp); } } printf("%lld\n",now.data); return 0; }
据azui神犇所说..CQOI都是 偏题,语文题,模板题 ...但是感觉今年的题目还是不错的,比较有价值
暴露的一些问题:
1.有题目可以想到正确做法,但是实现起来有差错,细节上的问题尤其容易出现
2.一些实用的算法和数据结构并不熟练,需要重视起来
3.调试的时间过长,发现问题不敏锐,严重影响进度,以后应该加强
启发:
1.对于想不出来最优解的问题,可以考虑想时间复杂度次优的方法,尽可能的优,在实际中能得到大把的分数,或者卡时A
2.数位DP方面需要总结一下技巧和方法,只有总结出了方法,才能面对各种题都不慌
3.认真读题,想题做题时要确保理解了题意,才能更深入的想出优秀的算法,很多时候应该按照题意去模拟
4.一些非反演的数论题,可能只是需要进行一些转化,然后多种东西嵌套求解,不用慌张;对于跟质因子相关的题目,入手点在质因子上,往往能得到高效的结果
【CQOI2016纯净整合】BZOJ-4519~4524 (5/6)
标签:
原文地址:http://www.cnblogs.com/DaD3zZ-Beyonder/p/5467855.html