CQOI2016

Day1

T1:

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

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

T1贴一发别人的代码。。

//Copyright(c)2016 liuchenrui
#include
#define short unsigned short
#define inf 1000000000
using namespace std;
template
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<


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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define inf (((unsigned long long)1 << (unsigned long long)63) - (unsigned long long)1)
#define Int long long
using 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> 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<


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

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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<

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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<


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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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  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<


你可能感兴趣的:(省选)