感觉CQOI的难度挺好的,比较贴近自身,所以拿出来做了一下


CQOI2016 Day1 T1:不同的最小割

涉及算法:最小割/分治/最小割树

思路:

最小割树裸题,直接分治最小割,记录下答案,最后排序一下,统计不同的答案即可

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';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=;
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<<],cur[maxn],S,T;
bool bfs()
{
memset(dis,-,sizeof(dis));
que[]=S; dis[S]=; int he=,ta=;
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]==-)
dis[edge[i].to]=dis[now]+,que[ta++]=edge[i].to;
}
return dis[T]!=-;
}
int dfs(int loc,int low)
{
if (loc==T) return low;
int w,used=;
for (int i=cur[loc]; i; i=edge[i].next)
if (edge[i].cap && dis[edge[i].to]==dis[loc]+)
{
w=dfs(edge[i].to,min(low-used,edge[i].cap));
edge[i].cap-=w; edge[i^].cap+=w;
used+=w; if (edge[i].cap) cur[loc]=i;
if (used==low) return low;
}
if (!used) dis[loc]=-;
return used;
}
#define inf 0x7fffffff
int dinic()
{
int tmp=;
while (bfs())
{
for (int i=; i<=n; i++) cur[i]=head[i];
tmp+=dfs(S,inf);
}
return tmp;
}
void init()
{
cnt=;
memset(ans,,sizeof(ans));
memset(head,,sizeof(head));
}
bool visit[maxn];
void DFS(int x)
{
visit[x]=;
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=; i<=cnt; i+=)
edge[i].cap=edge[i^].cap=(edge[i].cap+edge[i^].cap)>>;
S=id[L],T=id[R];
int maxflow=dinic();
memset(visit,,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-); work(r+,R);
}
int main()
{
// freopen("mincuto.in","r",stdin);
// freopen("mincuto.out","w",stdout);
init();
n=read(),m=read();
for (int i=; i<=n; i++) id[i]=i;
for (int u,v,w,i=; i<=m; i++)
u=read(),v=read(),w=read(),insert(u,v,w);
work(,n);
sort(ans+,ans+tot+);
int an=;
for (int i=; i<=tot; i++) if (ans[i]!=ans[i-]) an++;
printf("%d\n",an);
return ;
}

Day1T1


CQOI2016 Day1 T2:K远点对

涉及算法:凸包/KD-Tree/可并堆

思路:

先把所有点都放到KD Tree里,维护一个小根堆,枚举每个点。每次在kd树种查询的时候,就相当于当前的最优解是堆顶,像查最远点对一样在kd树里查就行了。

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<cstdlib>
using namespace std;
long long read()
{
long long x=,f=; char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-') f=-; ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
#define maxn 100010
#define inf 100000000000000000LL
int n,k,D;
long long sqr(long long a) {return (long long)(a*a);}
struct PointNode
{
int l,r; long long d[],maxx[],minn[];
bool operator < (const PointNode & A) const {return d[D]<A.d[D];}
PointNode (long long x=,long long y=) {l=r=; d[]=x; d[]=y;}
}p[maxn];
priority_queue<long long, vector<long long>, greater<long long> > heap;
long long dis(PointNode A,PointNode B) {return sqr(A.d[]-B.d[])+sqr(A.d[]-B.d[]);}
struct KDTreeNode
{
int rt;
PointNode Point,tree[maxn<<];
void Update(int now)
{
for (int i=; i<=; i++)
{
tree[now].minn[i]=tree[now].maxx[i]=tree[now].d[i];
if (tree[now].l)
tree[now].minn[i]=min(tree[tree[now].l].minn[i],tree[now].minn[i]),tree[now].maxx[i]=max(tree[tree[now].l].maxx[i],tree[now].maxx[i]);
if (tree[now].r)
tree[now].minn[i]=min(tree[tree[now].r].minn[i],tree[now].minn[i]),tree[now].maxx[i]=max(tree[tree[now].r].maxx[i],tree[now].maxx[i]);
}
}
int BuildTree(int l,int r,int dd)
{
int mid=(l+r)>>;
D=dd; nth_element(p+l,p+mid,p+r+);
tree[mid]=p[mid];
for (int i=; i<=; i++) tree[mid].minn[i]=tree[mid].maxx[i]=tree[mid].d[i];
if (l<mid) tree[mid].l=BuildTree(l,mid-,dd^);
if (r>mid) tree[mid].r=BuildTree(mid+,r,dd^);
Update(mid);
return mid;
}
long long dist(int pl,PointNode P)
{
long long re=;
for (int i=; i<=; i++)
re+=max(sqr(P.d[i]-tree[pl].minn[i]),sqr(P.d[i]-tree[pl].maxx[i]));
return re;
}
void Query(int now)
{
long long dl,dr,d0;
d0=dis(tree[now],Point);
if (d0>heap.top()) heap.pop(),heap.push(d0);
if (tree[now].l) dl=dist(tree[now].l,Point); else dl=-inf;
if (tree[now].r) dr=dist(tree[now].r,Point); else dr=-inf;
if (dl>dr)
{
if (dl>heap.top()) Query(tree[now].l);
if (dr>heap.top()) Query(tree[now].r);
}
else
{
if (dr>heap.top()) Query(tree[now].r);
if (dl>heap.top()) Query(tree[now].l);
}
}
}KDTree;
int main()
{
// freopen("farthest.in","r",stdin);
// freopen("farthest.out","w",stdout);
n=read(); k=read();
for (int x,y,i=; i<=n; i++) x=read(),y=read(),p[i]=PointNode(x,y);
KDTree.rt=KDTree.BuildTree(,n,);
for (int i=; i<=k+k; i++) heap.push(0LL);
for (int i=; i<=n; i++)
KDTree.Point=p[i],KDTree.Query(KDTree.rt);
printf("%lld\n",heap.top());
return ;
}

Day1T2


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[][][][][][][],l,r;
int p[],num;
long long cal(long long x)
{
memset(F,,sizeof(F));
long long ans=; int len=,digit[],a,b,c,d,e;
while(x){digit[++len]=x%; x/=;}
reverse(digit+,digit+len+);
F[][][][][][][]=;
for (int i=; i<=len-; i++)
for (int j=; j<=; j++)
for (int k1=; k1<=; k1++)
for (int k2=; k2<=; k2++)
for (int k3=; k3<=; k3++)
for (int k4=; k4<=; k4++)
for (int k5=; k5<=; k5++)
if (F[i][j][k1][k2][k3][k4])
for (int k=; k<=; k++)
{
if (k5 && (k>digit[i+])) continue;
if (k==j) a=; else a=;
if (k2==) b=(k1+a)==; else b=k2;
if (k3==) c=(k==); else c=k3;
if (k4==) d=(k==); else d=k4;
if ((c+d)==) continue;
if (k5 && (k==digit[i+])) e=; else e=;
F[i+][k][a][b][c][d][e]+=F[i][j][k1][k2][k3][k4][k5];
}
for (int i=; i<=; i++)
for (int k1=; k1<=; k1++)
for (int k3=; k3<=; k3++)
for (int k4=; (k4<=)&&(k4+k3<); k4++)
ans+=F[len][i][k1][][k3][k4][];
return ans;
}
int main()
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",cal(r+)-cal(l));
return ;
}

Day1T3


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=,f=; char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-') f=-; ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-''; 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=;
for (long long i=y; i; i>>=,x=(x+x)%p)
if (i&) re=(re+x)%p;
return re;
}
long long Quick_Pow(long long x,long long y,long long p)
{
long long re=;
for (long long i=y; i; i>>=,x=Quick_Mul(x,x,p))
if (i&) re=Quick_Mul(re,x,p);
return re;
}
void Exgcd(long long a,long long b,long long &x,long long &y)
{
if (b==) {x=; y=; 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==) return a;
return Gcd(b,a%b);
}
#define T 10007
long long Pollard_Rho(long long n)
{
long long x,y,cnt=,k=;
x=rand()%(n-)+; y=x;
while ()
{
cnt++;
x=(Quick_Mul(x,x,n)+T)%n;
long long gcd=Gcd(abs(x-y),n);
if (<gcd && gcd<n) return gcd;
if (x==y) return n;
if (cnt==k) y=x,k<<=;
}
}
int main()
{
srand(T);
e=read(),N=read(),c=read();
P=Pollard_Rho(N); Q=N/P;
r=(P-)*(Q-);
long long Inv=GetInv(e,r);
printf("%lld %lld",Inv,Quick_Pow(c,Inv,N));
return ;
}

Day2T1


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[];
#define maxn 1000100
int ch[maxn][],pos[maxn],rt=,cnt=,sz=,ti=;
void Insert(int *ip,int l,int x)
{
int now=rt;
for (int i=; 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=,m=-; top=;
for (int i=; i<=; i++)
{
if (!ch[now][ip[i]]) break;
now=ch[now][ip[i]];
if (pos[now] && pos[now]<=R)
stack[++top]=Node{pos[now],i+};
}
sort(stack+,stack+top+);
for (int i=; 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=; i<=m; i++)
{
char opt[]; scanf("%s",opt);
if (opt[]=='A')
{
ti++; int ip,len=,l;
memset(Ip,,sizeof(Ip));
for (int j=; j<=; j++)
{
scanf("%d.",&ip);
for (int k=; k>=; k--)
Ip[++len]=(&(ip>>k)); }
scanf("%d/",&ip);
for (int k=; k>=; k--) Ip[++len]=(&(ip>>k));
scanf("%d",&l);
//for (int j=1; j<=len; j++) printf("%d",Ip[j]); puts("");
Insert(Ip,l,ti);
}
if (opt[]=='Q')
{
int ip,len=,l,r;
memset(Ip,,sizeof(Ip));
for (int j=; j<=; j++)
{
scanf("%d.",&ip);
for (int k=; k>=; k--)
Ip[++len]=(&(ip>>k));
}
scanf("%d",&ip);
for (int k=; k>=; k--) Ip[++len]=(&(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 ;
}

Day2T2


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[]={,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,},cnt=;
int main()
{
scanf("%lld%d",&n,&k);
for (int i=; i<=cnt; i++)
for (x=j=; ; j++)
{
x*=(long long)prime[i]; if (x>n) break;
tmp.data=x,tmp.zs=j,tmp.nt=i-,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>)
for (int i=now.nt; i; i--)
{
tmp.data=(long long)now.data/prime[now.mp]*prime[i]; tmp.zs=now.zs-; 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 ;
}

Day2T3


据azui神犇所说..CQOI都是 偏题,语文题,模板题 ...但是感觉今年的题目还是不错的,比较有价值

暴露的一些问题:

1.有题目可以想到正确做法,但是实现起来有差错,细节上的问题尤其容易出现

2.一些实用的算法和数据结构并不熟练,需要重视起来

3.调试的时间过长,发现问题不敏锐,严重影响进度,以后应该加强

启发:

1.对于想不出来最优解的问题,可以考虑想时间复杂度次优的方法,尽可能的优,在实际中能得到大把的分数,或者卡时A

2.数位DP方面需要总结一下技巧和方法,只有总结出了方法,才能面对各种题都不慌

3.认真读题,想题做题时要确保理解了题意,才能更深入的想出优秀的算法,很多时候应该按照题意去模拟

4.一些非反演的数论题,可能只是需要进行一些转化,然后多种东西嵌套求解,不用慌张;对于跟质因子相关的题目,入手点在质因子上,往往能得到高效的结果

UPD:2016.05.24Day1T2(填坑完毕),附上全家福:

【CQOI2016纯净整合】BZOJ-4519~4524 (6/6)的更多相关文章

  1. bzoj 4519: [Cqoi2016]不同的最小割 最小割树

    怎么求一张无向图中任意两点之间的最小割? http://fanhq666.blog.163.com/blog/static/8194342620113495335724/ 一张无向图不同的最小割最多有 ...

  2. BZOJ 4519 [CQOI2016]不同的最小割

    这道题目很奇怪. 为什么奇怪?因为这道题用了一种叫分治最小割/最小割树的玩意. 以前从来没有见过这东西. 推荐一个讲这玩意的博客 写起来还是很顺手的. #include<iostream> ...

  3. bzoj 4519: [Cqoi2016]不同的最小割【最小割树Gomory–Hu tree】

    算法详见:http://www.cnblogs.com/lokiii/p/8191573.html 求出点两两之间的最小割之后,把他们扔到map/set里跑即可 可怕的是map和set跑的时间竟然完全 ...

  4. BZOJ 4519 不同的最小割 最小割树

    题面: 把每两个点当成源汇,求N*(N-1)个最小割中不同的有多少个 N<=850 分析: 有这样一个结论:一张无向图不同的最小割最多有n-1个. 那么我们一定可以建出一棵树,使得这棵树中每两个 ...

  5. Bzoj 4524 [Cqoi2016]伪光滑数(堆)

    题面 题解 先筛出$<128$的质数,很少,打个表即可 然后钦定一个质数最大,不断替换即可(丢进大根堆里面,然后取出一个,替换在丢进去即可) 具体来说,设一个四元组$[t,x,y,z]$表示当前 ...

  6. @bzoj - 4524@ [Cqoi2016]伪光滑数

    目录 @description@ @solution@ @version - 1@ @version - 2@ @accepted code@ @version - 1@ @version - 2@ ...

  7. bzoj千题计划140:bzoj4519: [Cqoi2016]不同的最小割

    http://www.lydsy.com/JudgeOnline/problem.php?id=4519 最小割树 #include<queue> #include<cstdio&g ...

  8. 4519: [Cqoi2016]不同的最小割

    4519: [Cqoi2016]不同的最小割 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 489 Solved: 301 [Submit][Stat ...

  9. BZOJ 4520: [Cqoi2016]K远点对

    4520: [Cqoi2016]K远点对 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 638  Solved: 340[Submit][Status ...

随机推荐

  1. JavaScript中清空数组的三种方式

    方式1,splice ? 1 2 3 var ary = [1,2,3,4]; ary.splice(0,ary.length); console.log(ary); // 输出 [],空数组,即被清 ...

  2. Linux 网络编程详解十二

    UDP的特点 --无连接 --基于消息的数据传输服务 --不可靠 --UDP更加高效 UDP注意点 --UDP报文可能会丢失,重复 --UDP报文可能会乱序 --UDP缺乏流量控制(UDP缓冲区写满之 ...

  3. zepto笔记 001

    $(function(){}) 在页面加载完成后运行的方法 等于window.onload; $("#id"),$(this) 都和jquery一样, tap方法不能阻止事件冒泡, ...

  4. codevs1910 递归函数

    难度等级:黄金 codevs1910 递归函数 题目描述 Description 对于一个递归函数w(a, b, c). 如果a <= 0 or b <= 0 or c <= 0就返 ...

  5. noi题库(noi.openjudge.cn) 1.5编程基础之循环控制T36——T45

    T36 计算多项式的值 描述 假定多项式的形式为xn+xn-1+-+x2+x+1,请计算给定单精度浮点数x和正整数n值的情况下这个多项式的值. 输入 输入仅一行,包括x和n,用单个空格隔开.x在flo ...

  6. TF400916错误修复办法

    在使用TFS作为研发过程管理工具的时候,如果调整了工作项的状态信息,可能会出现下面的错误: 要解决此问题非常简单: 1.找一台安装了VS2015程序的环境.因为我们使用的是TFS2015,所以需要对应 ...

  7. php json 格式控制

    本文同步至我的个人博客:http://www.52cik.com/2015/12/23/php-json-format.html 关于 json 这个问题,陆陆续续有新手朋友找我问,比如为什么我输出的 ...

  8. JavaScript 总结几个提高性能知识点

    前段时间花时间看了大半的<High Performance JavaScript>这本书啊,然后就开始忙项目了,庆幸最忙的一周已经熬过去了.由于空不出时间,这个月写的学习笔记也不多,忙完最 ...

  9. Scala集合操作

    大数据技术是数据的集合以及对数据集合的操作技术的统称,具体来说: 1.数据集合:会涉及数据的搜集.存储等,搜集会有很多技术,存储技术现在比较经典方案是使用Hadoop,不过也很多方案采用Kafka.  ...

  10. 数据库系统原理——ER模型与关系模型

    原文链接: http://blog.csdn.net/haovip123/article/details/21614887 犹记得第一次看<数据库系统原理>时看天书的感觉,云里雾里:现在已 ...