CQOI2016

来源:互联网 发布:java正则表达式冒号 编辑:程序博客网 时间:2024/05/13 22:09

Day1

T1:

    大意:一个850个点,8500条边的图,问所有点对之间的最小割有多少种不同的权值。
    分治最小割。(然而做这道题之前完全不知道是什么..%YY出来的lcr)。
    一开始所有点在一个集合中,随便找两个点求一次最小割,然后会把点集分成两半,继续直到所有点都变成一个点为止(反正我们这儿唯一A的人就是这么说的...但是奇慢无比,应该是姿势不太对...)。

    至于为什么这就是所有的n-1种最小割...我也不太清楚。

T1贴一发别人的代码。。

//Copyright(c)2016 liuchenrui#include<bits/stdc++.h>#define short unsigned short#define inf 1000000000using namespace std;template<typename T>inline void splay(T &v){v=0;char c=0;T p=1;while(c<'0' || c>'9'){if(c=='-')p=-1;c=getchar();}while(c>='0' && c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}v*=p;}struct Edge{short to,next;int flow;}edge[1000010];short first[851],size;short deep[851],dl[851];void addedge(short x,short y,int z){size++;edge[size].to=y;edge[size].next=first[x];first[x]=size;edge[size].flow=z;}void add(short x,short y,int z){addedge(x,y,z),addedge(y,x,0);}short aim;int dfs(short now,int flow){if(now==aim)return flow;int F=0;for(short u=first[now];u&&flow;u=edge[u].next){if(deep[edge[u].to]==deep[now]+1&&edge[u].flow){int tmp=dfs(edge[u].to,min(flow,edge[u].flow));F+=tmp;edge[u].flow-=tmp;edge[u^1].flow+=tmp;flow-=tmp;}}if(!F)deep[now]=-5;return F;}bool bfs(short S,short T){memset(deep,0,sizeof(deep));dl[1]=S;deep[S]=1;short head=0,tail=1;while(head!=tail){head++;for(int u=first[dl[head]];u;u=edge[u].next){if(!deep[edge[u].to]&&edge[u].flow){dl[++tail]=edge[u].to;deep[edge[u].to]=deep[dl[head]]+1;}}}return deep[T];}int maxflow(short S,short T){int ret=0;aim=T;while(bfs(S,T)){ret+=dfs(S,inf);}return ret;}short fr[8600],to[8600];int fl[8600];int ans[1000010];short cnt,n,m,p[851];void rebuild(){memset(first,0,sizeof first);size=1;for(int k=1;k<=m;k++){add(fr[k],to[k],fl[k]);add(to[k],fr[k],fl[k]);}}bool vis[860];void dfs2(short now){vis[now]=1;for(int u=first[now];u;u=edge[u].next){if(edge[u].flow&&!vis[edge[u].to]){dfs2(edge[u].to);}}}void cdq_calc(short l,short r){if(l>=r)return;rebuild();ans[++cnt]=maxflow(p[l],p[r]);memset(vis,0,sizeof vis);dfs2(p[l]);int t=l-1;for(short i=l;i<=r;i++){if(vis[p[i]]){swap(p[i],p[++t]);}}cdq_calc(l,t),cdq_calc(t+1,r);}int main(){splay(n),splay(m);for(short i=1;i<=m;i++){splay(fr[i]),splay(to[i]),splay(fl[i]);}for(short i=1;i<=n;i++){p[i]=i;}cdq_calc(1,n);sort(ans+1,ans+cnt+1);cnt=unique(ans+1,ans+cnt+1)-ans-1;printf("%d\n",cnt);//cerr<<clock()<<endl;}


T2:
    大意:求10^5个点中的第k远点对,k<=100.
    凸包+旋转卡壳+堆+暴力。
    正解是k-d树,但是我觉得我这个方法应该是正(Y)确(Y)的..

    首先YY了一个性质,对于两个凸包内的点a,b,一定有一个凸包上的点x使得max(ax,bx) > ab,有了这个性质我们就以知道,凸包内的一条边ab被访问,一定会在ax或者bx之后,而对于x点的最远点可以旋转卡壳得到。那么现在我们就定义凸包上的点是激活的,同时凸包内的点一开始是未激活的,一个点a被激活仅当ax是当前的最优解。比如现在的最优解是ax,我们令num[a]+1,num[x]+1,再求出a?的第num[a]+1大值,x?的num[x]+1大值,更新a和x的值,这个用堆维护就可以了。求第k大值也可以用堆维护(然而我并不会堆...强行线段树过之..),注意重点的情况,但是数据里好像没有(做凸包的时候注意一下就行了)...

#include <iostream>#include <cstring>#include <cstdlib>#include <string>#include <cstdio>#include <algorithm>#include <cmath>#include <ctime>#include <map>#define inf (((unsigned long long)1 << (unsigned long long)63) - (unsigned long long)1)#define Int long longusing namespace std;struct point {Int x;Int y;int id;};point p[100010],q[100010],stack[200010];Int val[800010],minx[850],maxx[850];int E,belong[100010],cnt,n,k;int head,tail,father[100010],t,num[100010];int tot[850];map < pair<int,int>,int> mp;Int calc(point x,point y,point z) {point A,B;A.x = x.x - z.x;A.y = x.y - z.y;B.x = y.x - z.x;B.y = y.y - z.y;return A.x * B.y - A.y * B.x;}Int dis(point x,point y) {Int ret = (x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y);return ret;}bool comp(const point &x,const point &y) {Int v = calc(x,y,p[1]);if(v == 0) return dis(x,p[1]) < dis(y,p[1]);return v > 0;}void build(int Now,int l,int r) {val[Now] = -1;if(l == r) return;int Mid = (l + r) >> 1;build(Now << 1,l,Mid);build(Now << 1 | 1,Mid + 1,r);}void change(int Now,int l,int r,int x,Int y) {if(l == r) {val[Now] = y;return ;}int Mid = (l + r) >> 1;if(x <= Mid) change(Now << 1,l,Mid,x,y);else change(Now << 1 | 1,Mid + 1,r,x,y);if(val[Now << 1] > val[Now << 1 | 1])val[Now] = val[Now << 1];else val[Now] = val[Now << 1 | 1];}int Ask(int Now,int l,int r) {if(l == r) return r;int Mid = (l + r) >> 1;if(val[Now << 1] == val[Now])return Ask(Now << 1,l,Mid);else return Ask(Now << 1 | 1,Mid + 1,r);}int find(int Now,int l,int r) {if(l == r) return r;int Mid = (l + r) >> 1;if(minx[Now] == minx[Now << 1])return find(Now << 1,l,Mid);else return find(Now << 1 | 1,Mid + 1,r);}void insert(int Now,int l,int r,int x,Int y) {if(l == r) {minx[Now] = maxx[Now] = y;tot[Now] = 1;return ;}int Mid = (l + r) >> 1;if(x <= Mid) insert(Now << 1,l,Mid,x,y);else insert(Now << 1 | 1,Mid + 1,r,x,y);tot[Now] = tot[Now << 1] + tot[Now << 1 | 1];minx[Now] = min(minx[Now << 1],minx[Now << 1 | 1]);maxx[Now] = max(maxx[Now << 1],maxx[Now << 1 | 1]);}int bf(int x) {int Z = num[x] + 1;for(int i = 1;i <= 810;i ++)minx[i] = inf,maxx[i] = -1,tot[i] = 0;for(int i = 1;i <= n;i ++){Int G = dis(q[i],q[x]);if(tot[1] < Z)insert(1,1,100,tot[1] + 1,G);else if(minx[1] < G) insert(1,1,100,find(1,1,100),G);}for(int i = 1;i <= n;i ++)if(dis(q[i],q[x]) == minx[1] && mp[make_pair(x,i)] == 0)return i;}int getint() {char c = 'd';int ret = 0;while(c < '0' || c > '9') c = getchar();while(c >= '0' && c <= '9') ret = ret * 10 + c - '0',c = getchar();return ret;}int main() {n = getint();k = getint();for(int i = 1;i <= n;i ++)p[i].x = getint(),p[i].y = getint(),q[i].x = p[i].x,q[i].y = p[i].y,p[i].id = i;t = 1;for(int i = 2;i <= n;i ++)if(p[i].y < p[t].y || (p[i].y == p[t].y && p[i].x < p[t].x))t = i;swap(p[1],p[t]);sort(p + 2,p + n + 1,comp);head = 1;tail = 0;for(int i = 1;i <= n;i ++) {while(head < tail && calc(p[i],stack[tail],stack[tail - 1]) >= 0)tail --;stack[ ++ tail] = p[i];}for(int i = 1;i <= tail;i ++)stack[i + tail] = stack[i];int M = 1;build(1,1,n);for(int i = 1;i <= tail;i ++){while(M < 2 * tail && dis(stack[M + 1],stack[i]) >= dis(stack[M],stack[i]))M = M + 1;change(1,1,n,stack[i].id,dis(stack[i],stack[M]));father[stack[i].id] = stack[M].id;}for(int i = 1;i < k;i ++) {int t = Ask(1,1,n),w = father[t];mp[make_pair(w,t)] = mp[make_pair(t,w)] = 1;num[t] ++;num[w] ++;father[t] = bf(t);father[w] = bf(w);change(1,1,n,t,dis(q[t],q[father[t]]));change(1,1,n,w,dis(q[w],q[father[w]]));}cout<<val[1];}


T3:
    求l到r,有至少连续3位相同,并且不同时出现8和4的数有多少种。(10^10<=l<=r<10^11)
    数位Dp。

    感觉没什么特别的..应该是人人都会的送分题那种....(为什么我写得那么长。。。。)

#include <iostream>#include <cstring>#include <cstdlib>#include <string>#include <cstdio>#include <algorithm>#include <cmath>#include <ctime>using namespace std;long long f[12][10][4][2][2],l,r;int getnum(long long x) {int ret = 0;while(x > 0) x /= 10,ret ++;return ret;}long long get(int x) {if(x == -1) return 0;long long ret = 1;for(int i = 1;i <= x;i ++)ret = 1ll * ret * 10;return ret;}long long Ask(long long x,int pos,int pre,int L,bool A,bool B) {if(pos == 0) return 0;if(A && B) return 0;long long ret = 0;int M = x / get(pos - 1);if(pos == 11) {if(x < get(pos - 1)) return 0;for(int i = 1;i <= M - 1;i ++)ret += f[pos][i][3][0][0] + f[pos][i][3][0][1] + f[pos][i][3][1][0];if(M == 4) ret += Ask(x - M * get(pos - 1),pos - 1,M,1,1,B);else if(M == 8)ret += Ask(x - M * get(pos - 1),pos - 1,M,1,A,1);else ret += Ask(x - M * get(pos - 1),pos - 1,M,1,A,B);return ret;}if(pos == 1) M = M + 1;if(L == 3) {if(A) {for(int i = 0;i <= M - 1;i ++)if(i != 8){ret += f[pos][i][1][0][0] + f[pos][i][1][1][0];ret += f[pos][i][2][0][0] + f[pos][i][2][1][0];ret += f[pos][i][3][0][0] + f[pos][i][3][1][0];}}else if(B) {for(int i = 0;i <= M - 1;i ++)if(i != 4){ret += f[pos][i][1][0][0] + f[pos][i][1][0][1];ret += f[pos][i][2][0][0] + f[pos][i][2][0][1];ret += f[pos][i][3][0][0] + f[pos][i][3][0][1];}}else {for(int i = 0;i <= M - 1;i ++){ret += f[pos][i][1][0][0] + f[pos][i][1][1][0] + f[pos][i][1][0][1];ret += f[pos][i][2][0][0] + f[pos][i][2][1][0] + f[pos][i][2][0][1];ret += f[pos][i][3][0][0] + f[pos][i][3][1][0] + f[pos][i][3][0][1];}}}else{if(A) {for(int i = 0;i <= M - 1;i ++)if(i != 8) {if(i == pre)for(int j = 3;j >= 3 - L;j --) ret += f[pos][i][j][0][0] + f[pos][i][j][1][0];else ret += f[pos][i][3][0][0] + f[pos][i][3][1][0];}}else if(B) {for(int i = 0;i <= M - 1;i ++)if(i != 4) {if(i == pre)for(int j = 3;j >= 3 - L;j --) ret += f[pos][i][j][0][0] + f[pos][i][j][0][1];else ret += f[pos][i][3][0][0] + f[pos][i][3][0][1];}}else {for(int i = 0;i <= M - 1;i ++){if(i == pre)for(int j = 3;j >= 3 - L;j --) ret += f[pos][i][j][0][0] + f[pos][i][j][1][0] + f[pos][i][j][0][1];else ret += f[pos][i][3][0][0] + f[pos][i][3][1][0] + f[pos][i][3][0][1];}}}x = x - 1ll * M * get(pos - 1);if(M == pre) ret += Ask(x,pos - 1,M,min(L + 1,3),(M == 4 || A),(M == 8 || B));else if(L == 3) ret += Ask(x,pos - 1,M,3,(M == 4 || A),(M == 8 || B));else ret += Ask(x,pos - 1,M,1,(M == 4 || A),(M == 8 || B));return ret;}void pre_work() {f[1][0][1][0][0] = 1;f[1][1][1][0][0] = 1;f[1][2][1][0][0] = 1;f[1][3][1][0][0] = 1;f[1][4][1][1][0] = 1;f[1][5][1][0][0] = 1;f[1][6][1][0][0] = 1;f[1][7][1][0][0] = 1;f[1][8][1][0][1] = 1;f[1][9][1][0][0] = 1;for(int i = 2;i <= 11;i ++)for(int j = 0;j <= 9;j ++)for(int w = 0;w <= 9;w ++)for(int k = 1;k <= 3;k ++) for(int g = 0;g <= 1;g ++)for(int h = 0;h <= 1;h ++) {if(j == 4) {if(k == 3) f[i][j][3][1][h] += f[i - 1][w][3][g][h];else if(j == w) f[i][j][min(k + 1,3)][1][h] += f[i - 1][w][k][g][h];else f[i][j][1][1][h] += f[i - 1][w][k][g][h];}else if(j == 8) {if(k == 3) f[i][j][3][g][1] += f[i - 1][w][3][g][h];else if(j == w) f[i][j][min(k + 1,3)][g][1] += f[i - 1][w][k][g][h];else f[i][j][1][g][1] += f[i - 1][w][k][g][h];}else {if(k == 3) f[i][j][3][g][h] += f[i - 1][w][3][g][h];else if(j == w) f[i][j][min(k + 1,3)][g][h] += f[i - 1][w][k][g][h];else f[i][j][1][g][h] += f[i - 1][w][k][g][h];}}}int main() {cin>>l>>r;pre_work();cout<<Ask(r,11,-1,1,0,0) - Ask(l - 1,11,-1,1,0,0);}

Day2
    T1:题意感觉有点绕。。搞清楚已知条件之后发现就是一个大质数分解+一个拓展欧几里锝。直接上pollard rho即可。

#include <iostream>#include <cstring>#include <cstdlib>#include <string>#include <cstdio>#include <algorithm>#include <cmath>#include <ctime>using namespace std;long long E,N,c,p = 0,q,k,r,A,n,B;long long ksc(long long x,long long y) {long long ret = 0;while(y > 0) {if(y % 2 == 1) ret = (ret + x) % N;x = (x + x) % N;y = y / 2;}return ret;}long long f(long long x) {return (1ll * ksc(x,x) + 1ll * k) % N;}long long gcd(long long x,long long y) {if(y == 0) return x;return gcd(y,x % y);}long long find(long long x) {k = 1ll * ((rand() * rand() % N) * (rand() * rand() % N)) % N;long long a = f(1);long long b = f(f(1));while(a != b){long long y = abs(a - b);if(gcd(y,x) != 1) return gcd(x,y);a = f(a);b = f(f(b));}return 0;} void work(long long x) {p = find(x);while(p == 0)p = find(x);q = x / p;}long long exgcd(long long x,long long y) {if(y == 0) {A = 1;B = 0;return x;}long long ret = exgcd(y,x % y);long long t = A;A = B;B = t - (x / y) * B;return ret;}long long ksm(long long x,long long y) {long long ret = 1;while(y > 0) {if(y % 2 == 1) ret = ksc(ret,x);x = ksc(x,x);y = y / 2;}return ret;}int main() {srand(127);cin>>E>>N>>c;work(N);r = (p - 1ll) * (q - 1ll);long long C = exgcd(E,r);long long t = r / C;A = ((A * (1 / C)) % t + t) % t;n = ksm(c,A);cout<<A<<' '<<n;}


    T2:题意还是有点绕。。搞清楚之后发现这不是裸的trie。。。仔细看了之后发现确实是这样。。然后我开了一个单调队列就可以了。

#include <iostream>#include <cstring>#include <cstdlib>#include <string>#include <cstdio>#include <algorithm>#include <cmath>#include <ctime>using namespace std;int son[32000010][2],len,A[10],n,tot = 0,tim[32000010],cnt,s[50],q[100];int getint() {int ret = 0;char g = 'd';while(g < '0' || g > '9') g = getchar();while(g >= '0' && g <= '9') ret = ret * 10 + g - '0',g = getchar();return ret;}void insert(int x) {int Now = 0;for(int i = 1;i <= len;i ++) {int t = s[i] - 0;if(son[Now][t] == 0) {son[Now][t] = ++ tot;Now = tot;}else Now = son[Now][t];}if(tim[Now] == 0) tim[Now] = x;}int Ask() {int ret = 0;int Now = 0;int tail = 0;for(int i = 1;i <= len;i ++) {int t = s[i] - 0;Now = son[Now][t];if(Now == 0) break;if(tim[Now] != 0) {while(tail && tim[q[tail]] > tim[Now]) tail --;q[ ++tail] = Now;}}for(int i = 1;i <= tail;i ++) if(tim[q[i]] >= A[5] && tim[q[i]] <= A[6])ret ++;return ret;}int main() {scanf("%d",&n);for(int i = 1;i <= n;i ++){char c = 'd';while(c != 'A' && c != 'Q') c = getchar();if(c == 'A') {for(int j = 1;j <= 5;j ++)A[j] = getint();for(int j = 1;j <= 4;j ++)for(int k = 8;k >= 1;k --)if(A[j] >= (1 << (k - 1)))s[(j - 1) * 8 + (8 - k + 1)] = 1,A[j] -= (1 << (k - 1));else s[(j - 1) * 8 + (8 - k + 1)] = 0;len = min(32,A[5]);insert( ++cnt);}else {for(int j = 1;j <= 6;j ++)A[j] = getint();for(int j = 1;j <= 4;j ++)for(int k = 8;k >= 1;k --)if(A[j] >= (1 << (k - 1)))s[(j - 1) * 8 + (8 - k + 1)] = 1,A[j] -= (1 << (k - 1));else s[(j - 1) * 8 + (8 - k + 1)] = 0;len = 32;printf("%d\n",Ask());}}return 0;}


    T3:题意还是有点绕。。(其实这个是我蠢了。。)原题中p^k<=N我以为k是p出现的次数。。结果是总的因子出现个数。。导致我写完1,2题之后直接就懵了。。后来询问Claris做法后看他的代码才发现我看错题了。。既然k是总的因子出现个数,于是就限定最大因子和总的因子个数,定义结构体val,x,y,z分别表示当前的值,剩余的最大因子的个数,其他因子中的最小数,最大因子是什么,每次考虑把最大因子减少一个并替换成更小的因子,记录其他因子的最小值是为了去重。然后搞一个优先队列就可以了。

#include <iostream>#include <cstring>#include <cstdlib>#include <string>#include <cstdio>#include <algorithm>#include <queue>#include <cmath>#include <ctime>using namespace std;long long n,k;int p[100],tot = 0;bool visit[200];struct cp {  long long val;int x,y,z;  cp(){}  cp(long long _v,int _x,int _y,int _z){val = _v,x = _x,y = _y,z = _z;}  bool operator < (const cp &b)const{return val < b.val;}};priority_queue <cp> Q;int main() {//freopen("lx.in","r",stdin);//freopen("lx.out","w",stdout);cin>>n>>k;for(int i = 2;i <= 128;i ++){if(visit[i] == false) p[ ++tot] = i;for(int j = 1;j <= tot && p[j] * i <= 128;j ++){visit[i * p[j]] = true;if(i % p[j] == 0) break;}}for(int i = 1;i <= tot;i ++) {long long x = 1;for(int j = 1;x <= n / p[i];j ++)Q.push(cp(1ll * x * p[i],j,i - 1,i)),x = 1ll * x * p[i];}for(int i = 1;i <= k - 1;i ++) {cp t = Q.top();Q.pop();if(t.x != 1) for(int j = t.y;j >= 1;j --)Q.push(cp(1ll * t.val / p[t.z] * p[j],t.x - 1,j,t.z));}cp t = Q.top();cout<<t.val;} 


0 0
原创粉丝点击