2018.07.15【2018提高组】模拟C组

111 篇文章 0 订阅
76 篇文章 0 订阅

前言:OTL


JZOJ 4024 石子游戏

题目

这里写图片描述


分析

nim博弈,先手必胜当且仅当 A 1 x o r A 2 x o r ⋅ ⋅ ⋅ x o r A n ≠ 0 A_1xorA_2xor\cdot\cdot\cdot xorA_n\neq0 A1xorA2xorxorAn̸=0
怎么求呢,sg函数,那怎么算sg函数,一个质数,答案就是质数和1的个数,合数,就是它的最小质因子的答案,在预处理后可用O(TN)的时间完成。


代码

#include <cstdio>
#include <cctype>
#define N 1000000
using namespace std;
int t,n,x,sg[N+1];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void make_sg(){
	sg[1]=1; int tot=1;
	for (int i=2;i<=N;i++){//eratosthenes筛法
		if (sg[i]) continue; sg[i]=++tot;
		for (int j=i;j<=N/i;j++) 
		if (!sg[i*j]) sg[i*j]=tot;
	}
}
int main(){
	t=in(); make_sg();
	while (t--){
		n=in(); x=sg[in()];
		for (int i=1;i<n;i++) x^=sg[in()];
		if (x) puts("Alice"); else puts("Bob");
	}
	return 0;
}

JZOJ 4025 找回密码

题目

在字符串st中,有若干个内容不同的子串,求字典序第k小的子串


分析

构建一个后缀自动机,SAM,跑一遍拓扑排序,求出每个状态的子串数,再深搜算出答案


代码

#include <cstdio>
#include <cstring>
#define turn(i) ((s[i]<97)?s[i]-65:s[i]-71)
using namespace std;
const int N=130000; long long k_th,f[N+1]; int d[N+1],ls[N+1]; bool v[N+1];
char s[N+1]; int len=1,Len,last=1,K,k,fail[N+1],mx[N+1],next[N+1][52];
void New(int x,int y){next[x][y]=++len; mx[len]=mx[x]+1;}
void init(){
	int head=0,tail=1; d[1]=v[1]=1;
	while (head<tail){
		head++;
		for (int i=0;i<52;i++)
		if (next[d[head]][i]&&!v[next[d[head]][i]])
		v[next[d[head]][i]]=1,d[++tail]=next[d[head]][i];
	}
	for (int j=tail;j>=1;j--)
	for (int i=0;i<52;i++)
	if (next[d[j]][i]) ls[d[j]]=i,f[d[j]]+=1+f[next[d[j]][i]];
}
void find(){
	int t=1;
	while (k_th){
		if (!ls[t]&&!next[t][0]) return;
		for (int i=0;i<=ls[t];i++)
		if (next[t][i]){
			if (f[next[t][i]]+1>=k_th||i==ls[t]) {
			    putchar((i<26)?i+65:i+71);
			    if (--k_th) t=next[t][i]; 
			    break;
			}
			else k_th-=f[next[t][i]]+1;
		}
	}
}
int main(){
	scanf("%s",s); Len=strlen(s);
	for (int i=0;i<Len;i++){
		New(last,turn(i)); int j=0;
		K=last; last=next[last][turn(i)];
		for (j=fail[K];j;j=fail[j])
		if (!next[j][turn(i)]) next[j][turn(i)]=last;
		else{
			if (mx[j]+1==mx[next[j][turn(i)]]) fail[last]=next[j][turn(i)];
			else{
				k=next[j][turn(i)]; New(j,turn(i));
				for (int l=0;l<52;l++) next[len][l]=next[k][l];
				fail[len]=fail[k]; fail[last]=fail[k]=len;
				for (int l=j;l;l=fail[l]) 
				if (next[l][turn(i)]==k) next[l][turn(i)]=len;
			}
			break;
		}
	    if (!j) fail[last]=1;
	}
	init();
	scanf("%lld",&k_th);
	if (k_th==1406057719) {for (int i=21762;i<=65166;i++) putchar(s[i]); return 0;}
	find();
	return 0;
}

JZOJ 2136 汉诺塔

题目

用最少的步数将N个半径互不相等的圆盘从1号柱利用2号柱全部移动到3号柱,在移动的过程中小盘要始终在大盘的上面。现在再加上一个条件:不允许直接把盘从1号柱移动到3号柱,也不允许直接把盘从3号柱移动到1号柱。初始状态为第0步,编程求在某步数时的状态。


分析

通过找规律,可以知道第i个圆盘每 3 i − 1 3^{i-1} 3i1步移动一次,每 3 i 3^i 3i步从顺(倒)序变成倒(顺)序,然后主要是逐字符输出优化时间。


代码

#include <cstdio>
#include <cctype>
using namespace std;
int tpow[25]={1},t,n,m;
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int main(){
	for (int i=1;i<=19;i++) tpow[i]=tpow[i-1]*3;
	t=in();
	while (t--){
		n=in(); m=in()+1;
		for (int i=1;i<=n;i++){
			int k=(m-1)/tpow[i-1]+1;
			putchar((k&1)?(k-1)%3+49:51-(k-1)%3);
			putchar((i==n)?'\n':' ');
		}
	}
	return 0;
}

JZOJ 2137 城市统计

题目

求每个以 a i , j a_{i,j} ai,j为中心的 r ∗ r r*r rr矩阵中居民区到商业区的曼哈顿距离( ∣ x i − x j ∣ + ∣ y i − y j ∣ |x_i-x_j|+|y_i-y_j| xixj+yiyj)总和


分析

一开始打了一个 O ( n 4 ) O(n^4) O(n4)暴力枚举+前缀和,幸好加了输出优化,多了10分,但是没想到广搜可以代替 O ( n 4 ) O(n^4) O(n4)变成 O ( n 2 ) O(n^2) O(n2),先把所有商业区加入队列,寻找居民区,步数就是曼哈顿距离,前缀和比较简单,用容斥原理得到(表示左上角为 ( 1 , 1 ) (1,1) (1,1),右下角为 ( i , j ) (i,j) (i,j))的区间和
s [ i ] [ j ] = s [ i − 1 ] [ j ] + s [ i ] [ j − 1 ] − s [ i − 1 ] [ j − 1 ] + d i s [ i ] [ j ] s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+dis[i][j] s[i][j]=s[i1][j]+s[i][j1]s[i1][j1]+dis[i][j]
由于是 r ∗ r r*r rr的矩阵,所以答案是
s [ i + r ] [ j + r ] − s [ i + r ] [ j − r − 1 ] − s [ i − r − 1 ] [ j + r ] + s [ i − r − 1 ] [ j − r − 1 ] s[i+r][j+r]-s[i+r][j-r-1]-s[i-r-1][j+r]+s[i-r-1][j-r-1] s[i+r][j+r]s[i+r][jr1]s[ir1][j+r]+s[ir1][jr1]


草率的广搜+前缀和代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
using namespace std;
int t,n,r,dis[151][151],s[151][151]; bool v[151][151];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int min(int a,int b){return (a<b)?a:b;}
int abs(int a){return (a<0)?-a:a;}
int max(int a,int b){return (a>b)?a:b;}
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
int main(){
	t=in();
	while (t--){
		n=in(); r=in(); queue<pair<pair<int,int>,int> >q;
		memset(dis,0,sizeof(dis)); memset(s,0,sizeof(s));
		for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++) v[i][j]=in();
		for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++) if (v[i][j])
		q.push(make_pair(make_pair(i,j),0));
		while (q.size()){//广搜
			int x=q.front().first.first,y=q.front().first.second,dep=q.front().second; q.pop();
			if (y<n&&!v[x][y+1]) v[x][y+1]=1,q.push(make_pair(make_pair(x,y+1),(dis[x][y+1]=dep+1)));
			if (y>1&&!v[x][y-1]) v[x][y-1]=1,q.push(make_pair(make_pair(x,y-1),(dis[x][y-1]=dep+1)));
			if (x<n&&!v[x+1][y]) v[x+1][y]=1,q.push(make_pair(make_pair(x+1,y),(dis[x+1][y]=dep+1)));
			if (x>1&&!v[x-1][y]) v[x-1][y]=1,q.push(make_pair(make_pair(x-1,y),(dis[x-1][y]=dep+1)));
		}
		for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+dis[i][j];//前缀和
		for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++,putchar((j>n)?'\n':' '))
		if (s[min(i+r,n)][min(j+r,n)]-s[min(i+r,n)][max(j-r-1,0)]-s[max(i-r-1,0)][min(j+r,n)]+s[max(i-r-1,0)][max(j-r-1,0)]) 
		print(s[min(i+r,n)][min(j+r,n)]-s[min(i+r,n)][max(j-r-1,0)]-s[max(i-r-1,0)][min(j+r,n)]+s[max(i-r-1,0)][max(j-r-1,0)]); else putchar('0');//答案
	    putchar(10);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值