CQOI2014

CQOI每年都这样啦。。半水不水的。。

我要在大CQ多好啊~~


T1:

第一题深搜水水的骗了55分...弱爆啊。。ORZ深搜90分。。

搜第一行就可以确定下面的行。。第一行还可以折半搜索在对称过去。。!

然后就是2^(m/2)*nm。。 然后还是要T两三个点 。。囧

不过不要紧的,BZOJ算总时间能过。。HAHAHAHA

正解:异或方程组。。待我研究研究。。


DFS版:(T3)

#include <cstdio>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
#define per(i,r,l) for (int i=r;i>=l;--i)
int map[45][45];
int n,m;
#define pd(i,j) (map[i][j]^map[i-1][j]^map[i][j-1]^map[i][j+1])
void check(){
	rep(i,2,n) rep(j,1,m){
		if (pd(i-1,j)) map[i][j]=1;
		   else map[i][j]=0;
		}
	rep(i,1,m)if (pd(n,i)) return;
	rep(i,1,n) rep(j,1,m) printf("%d%c",map[i][j],j==m?'\n':' ');
	exit(0);
}
void dfs1(int v){
	if (v>(m+1)/2){
		int mid=(m+1)/2+1;
		per(i,m,mid) map[1][i]=map[1][m-i+1];
		check();
		return;
		}
	map[1][v]=1;dfs1(v+1);
	map[1][v]=0;dfs1(v+1);
}
int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	scanf("%d%d",&n,&m);
	dfs1(1);
}


T2:

看上去肯定是个网络流。

当时想到用满流来判断,但是不能对应a1->a2 b1->b2 可能流量是a1->b2 。。。然后,。。就没有然后了。。当场没交。。。

(后来发现这......竟然还有30分。。)


标算:先S-a1 a2-T  S-b1 b2-T 连一遍跑一次。若不满流肯定无解。

若满流就再  S-a2 a1-T S-b1 b2-T 再跑一次。若不满流则无解。

若两次都满流则有解。


如果只跑一次是有BUG的,,,因为不能保证a1->a2的是an。

如果a1流a2的是an-x 那么肯定 a1->b2的是x   ,b1->b2是bn-x  b1->a2 是x


下面如果 b2->a2可以流x 那就可以修正路径了, a1->b2能流x  b2->a2能流x 那么肯定存在a1->a2流x  。而之前又有a1->a2流an-x 那么肯定存在a1->a2流an

然后建个图随便怎么搞都行了。。。

#include <cstdio>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
#define per(i,r,l) for (int i=r;i>=l;--i)
#define gt getchar()
bool upmin(int &a,const int &b){return a>b?a=b,1:0;}
const int MAX_N=55;
const int INF=~0U>>1;
int first[MAX_N],next[MAX_N*MAX_N*2],to[MAX_N*MAX_N*2],f[MAX_N*MAX_N*2];
int tal;
void tjb(int x,int y,int F){
	next[++tal]=first[x];
	first[x]=tal,to[tal]=y,f[tal]=F;
	next[++tal]=first[y];
	first[y]=tal,to[tal]=x,f[tal]=0;
}
int pre[MAX_N],cur[MAX_N],his[MAX_N],dis[MAX_N],gap[MAX_N];
int sap(int S,int T){
	rep(i,S,T) cur[i]=first[i],dis[i]=gap[i]=0;
	int u=pre[S]=S;
	int aug=INF,maxflow=0;
	int Size=T-S+1;
	gap[0]=Size;
	while (dis[S]<Size){
		his[u]=aug;
		for (int& k=cur[u];k;k=next[k])
			if (f[k]&&dis[u]==dis[to[k]]+1) break;
		if (cur[u]){
			int v=to[cur[u]];
			upmin(aug,f[cur[u]]);
			pre[v]=u,u=v;
			if (u==T){
				maxflow+=aug;
				while (u!=S) u=pre[u],f[cur[u]]-=aug,f[cur[u]^1]+=aug;
				aug=INF;
				}
		}else{
			int mindis=Size;
			for (int k=first[u];k;k=next[k])
				if (f[k]&&upmin(mindis,dis[to[k]]+1)) cur[u]=k;
			if (!--gap[dis[u]]) break;
			++gap[dis[u]=mindis];
			u=pre[u],aug=his[u];
			}
		}
	return maxflow;
}
char map[MAX_N][MAX_N];
int n,a1,a2,an,b1,b2,bn,S,T;
void mkgraph(){
	std::fill(first,first+n+10,0);tal=1;
	tjb(S,a1+1,an),tjb(a2+1,T,an);
	tjb(S,b1+1,bn),tjb(b2+1,T,bn);
	rep(i,1,n) rep(j,1,n){
		if (map[i][j]=='N') tjb(i,j,INF);
		if (map[i][j]=='O') tjb(i,j,1);
		}
}
int main(){
	freopen("bridge.in","r",stdin);
	freopen("bridge.out","w",stdout);
	while(scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn)!=EOF){
		S=0,T=n+1;
		rep(i,1,n) rep(j,1,n){
			char& t=map[i][j];
			for (t=gt;t!='X'&&t!='N'&&t!='O';t=gt);
			}
		mkgraph();
		if (sap(S,T)!=an+bn){printf("No\n");continue;}
		std::swap(a1,a2);
		mkgraph();
		if (sap(S,T)!=an+bn){printf("No\n");continue;}
		printf("Yes\n");
		}
}

T3:

这题A掉了。。

首先可以C(n*m,3)  。。然后再减掉不行的情况。

不行->只有三点共线

所以考虑找到每一条极长直线中有多少个点。然后再减一下就行了。

由于n、m<=1000

我们可以枚举直线(枚举一个矩形长宽,对角线是我们想要的线段,矩形的长宽Gcd+1就是这条线段上的点数。)

然后令s[n]表示 线段上有n个点 的线段条数 

注意到这不是极长的。但是 若 一条线段上有n个点 那么他肯定包含2条n-1个点的线段  3条n-2的线段。。。

然后就可以倒推出有n个点的极长线段的条数。。

然后再组合一下就行了。。


后面发现我这方法有点沙茶了。。

直接枚举线段,,然后我们必选这个线段的端点,然后端点中间的点随便选,个数就是Gcd-1。

然后不会重复直接减掉就行了。。


#include <cstdio>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
#define per(i,r,l) for (int i=r;i>=l;--i)
typedef long long LL;
int Gcd(int a,int b){return b?Gcd(b,a%b):a;}
const int MAX_N=1050;
int n,m;
LL C[MAX_N][5];
void getC(){
	int p=n>m?n:m;
	rep(i,0,p) C[i][0]=1;
	rep(i,1,p) rep(j,1,3) C[i][j]=C[i-1][j-1]+C[i-1][j];
}
LL num[MAX_N];
LL calc(){
	LL res=0;
	rep(i,1,n) rep(j,1,m){
		int s=(n-i+1)*(m-j+1);
		int g=Gcd(i-1,j-1)+1;
		num[g]+=((i==1||j==1)?1:2)*s;
		}
	int p=n>m?n:m;
	per(i,p,1) rep(j,i+1,p) num[i]-=(j-i+1)*num[j];
	rep(i,3,p) res+=C[i][3]*num[i];
	return res;
}
int main(){
	freopen("triangle.in","r",stdin);
	freopen("triangle.out","w",stdout);
	scanf("%d%d",&n,&m);n++,m++;
	getC();
	LL ans=(LL)(n*m)*(n*m-1)*(n*m-2)/6-calc();
	printf("%I64d\n",ans);
}



T4:

逗比了。。

没想到如何找最小值的位置啊。。。


泰沙茶 了。。。 记录最小值的节点编号。 然后splay那个节点到根,然后左儿子size+1.....

蒟蒻。。。


以前写splay都是先用size找的时候就downdata了。。

但是这个直接splay一个点,必须先把上面的data全部down下来才行!

然后好像rotate的时候就不用down了??!

downdata可以用dfs来做。。要从上而下。。虽然我之前傻傻地开了个数组来记preprepreprepre...

#include <cstdio>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
#define per(i,r,l) for (int i=r;i>=l;--i)
const int MAX_N=100050;
const int INF=~0U>>1;
int getx(){
	char c;int x;
	for (c=getchar();c<'0'||c>'9';c=getchar());
	for (x=0;c>='0'&&c<='9';c=getchar())
		x=(x<<3)+(x<<1)+c-'0';
	return x;
}
struct ND{int c,v;} value[MAX_N];
bool operator <(const ND &a,const ND &b){return a.c<b.c||(a.c==b.c&&a.v<b.v);}
int a[MAX_N],n;
int pre[MAX_N],ch[MAX_N][2],tal=0;
int size[MAX_N];
ND r[MAX_N],min[MAX_N];
bool rev[MAX_N];
int root;
void up(int x){
	size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
	min[x]=min[ch[x][0]]<min[ch[x][1]]?min[ch[x][0]]:min[ch[x][1]];
	min[x]=min[x]<r[x]?min[x]:r[x];
}
void down(int x){
	if (rev[x]){
		rev[ch[x][0]]^=1;
		rev[ch[x][1]]^=1;
		rev[x]=0;
		std::swap(ch[x][0],ch[x][1]);
		}
}
void Rotate(int x,int f){
	int y=pre[x];
	down(x),down(y); //似乎可以不要了吧
	ch[y][!f]=ch[x][f];
	pre[ch[x][f]]=y;
	ch[x][f]=y;
	pre[x]=pre[y];
	pre[y]=x;
	if (pre[x]) ch[pre[x]][ch[pre[x]][1]==y]=x;
	up(y);
}

void pdown(int x){
	if (!x) return;
	pdown(pre[x]);down(x);
}
//int fa[MAX_N],top,p;
void splay(int x,int goal){
	//for (top=0,p=x;p;p=pre[p]) fa[++top]=p;
	//per(i,top,1) down(fa[i]);
	pdown(x);
	while(pre[x]!=goal){
		if (pre[pre[x]]==goal)
			Rotate(x,ch[pre[x]][0]==x);
		else{
			int y=pre[x],z=pre[y];
			int f=(ch[z][0]==y);
			if (ch[y][f]==x)
				Rotate(x,!f),Rotate(x,f);
			else
				Rotate(y,f),Rotate(x,f);
			}
		}
	up(x);
	if (goal==0) root=x;
}
void RTO(int x,int goal){
	int v=root;
	for (;v;){
		down(v);
		int tp=size[ch[v][0]]+1;
		if (tp==x) break;
		if (tp<x) x-=tp,v=ch[v][1];
			else v=ch[v][0];
		}
	down(v);
	splay(v,goal);
}
void range(int l,int r){
	RTO(l,0);RTO(r+2,root);
}
void Newn(int &v,int p,int fa){
	v=++tal;
	r[v]=min[v]=(ND){a[p],v};
	size[v]=1;pre[v]=fa;
	rev[v]=0;
}
void build(int &v,int l,int r,int fa){
	if (l>r) return;
	int mid=l+r>>1;
	Newn(v,mid,fa);
	build(ch[v][0],l,mid-1,v);
	build(ch[v][1],mid+1,r,v);
	up(v);
}

ND qmin(int L,int R){
	range(L,R);
	return min[ch[ch[root][1]][0]];
}
void reverse(int L,int R){
	range(L,R);
	rev[ch[ch[root][1]][0]]^=1;
}
int main(){
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	min[0].c=INF;
	n=getx();
	rep(i,1,n) value[i]=(ND){getx(),i};
	std::sort(value+1,value+n+1);
	rep(i,1,n) a[value[i].v]=i;
	per(i,n,1) a[i+1]=a[i];
	build(root,1,n+2,0);
	ND tp;
	rep(i,1,n){
		tp=qmin(i,n);
		splay(tp.v,0);
		printf("%d%c",size[ch[root][0]],i==n?'\n':' ');
		reverse(i,size[ch[root][0]]);
		}
}



T5:

第一眼: 这不是冬令营的什么自动机吗。。什么DFANFA。。。

然后就模拟了一遍。。。

然后就木有然后了。。30分算法还WA了个点。。沙茶代码就不发了。。现在还没做呢。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值