0625杂题

A

我们考虑实现查询黑王移动到某个位置会不会被将死。

我们发现,如果黑王的四个移动可能都会出界或被将死,就是 YES,否则 NO

然后,我们考虑全盘的 \(N\) 个棋子分别能否将死王。我们发现,在王位置的就被吃掉了。对于其他棋子:

  • 如果是王,检查是否在同一列,并且中间没有其他棋子。

  • 如果是马,蹩马腿不太好用字符数组处理,但可以暴力枚举八个方向,暴力检查蹩马腿,都列出来不会很复杂。

  • 如果是车,检查是否同行同列,之间没有其他棋子

  • 如果是炮,检查是否同行同列,之间其他棋子数量为 \(1\)

我们发现,我们除了马以外,只需要实现某行、某列某个区间中棋子的个数即可。

int n,xs,ys,X[10],Y[10],a[11][10],oqp=0;
char C[10];
inline int cnt_lin(int x,int l,int r){
	if(l>r)swap(l,r);
	int res=0;
	rep(i,l,r)res+=a[x][i];
	return res;
}
inline int cnt_col(int x,int l,int r){
	if(l>r)swap(l,r);
	int res=0;
	rep(i,l,r)res+=a[i][x];
	return res;
}
inline bool check_can(int x,int y,int xx,int yy){
	if(x==xx&&cnt_lin(x,y,yy)==2+oqp)return 1;
	if(y==yy&&cnt_col(y,x,xx)==2+oqp)return 1;
	return 0;
}
inline bool check_hor(int x,int y,int xx,int yy){
	if(!a[x-1][y]){
		if(mp(x-2,y-1)==mp(xx,yy))return 1;
		if(mp(x-2,y+1)==mp(xx,yy))return 1;
	}
	if(!a[x+1][y]){
		if(mp(x+2,y-1)==mp(xx,yy))return 1;
		if(mp(x+2,y+1)==mp(xx,yy))return 1;
	}
	if(!a[x][y-1]){
		if(mp(x+1,y-2)==mp(xx,yy))return 1;
		if(mp(x-1,y-2)==mp(xx,yy))return 1;
	}
	if(!a[x][y+1]){
		if(mp(x+1,y+2)==mp(xx,yy))return 1;
		if(mp(x-1,y+2)==mp(xx,yy))return 1;
	}return 0;
}
inline bool check_cha(int x,int y,int xx,int yy){
	if(x==xx&&cnt_lin(x,y,yy)==1+oqp)return 1;
	if(y==yy&&cnt_col(y,x,xx)==1+oqp)return 1;
	return 0;
}
inline bool check_gen(int x,int y,int xx,int yy){
	if(y==yy&&cnt_col(y,x,xx)==1+oqp)return 1;
	return 0;
}
inline bool check(int x,int y){//¼ì²éºÚÍõÔÚ(x,y)ÊÇ·ñ±»½«¾ü
	if(x<1||x>3||y<4||y>6)return 1;
	oqp=0;
	int res=0;
	rp(i,n)if(X[i]==x&&Y[i]==y)oqp=1;
	rp(i,n)if(X[i]!=x||Y[i]!=y){
		if(C[i]=='G')res|=check_gen(X[i],Y[i],x,y);
		else if(C[i]=='R')res|=check_cha(X[i],Y[i],x,y);
		else if(C[i]=='H')res|=check_hor(X[i],Y[i],x,y);
		else if(C[i]=='C')res|=check_can(X[i],Y[i],x,y);
	}return res;
}
inline void solve(){
	rp(i,10)rp(j,9)a[i][j]=0;
	rp(i,n){
		cin>>C[i]>>X[i]>>Y[i];
		a[X[i]][Y[i]]=1;
	}
	int res=(check(xs+1,ys)&check(xs-1,ys)&check(xs,ys+1)&check(xs,ys-1));
	if(res)cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>xs>>ys;
	while(n){
		solve();
		cin>>n>>xs>>ys;
	}
	return 0;
}
//Nyn-siyn-hert

B

我们可以枚举正方形,然后暴力检查边框是否都被填满。

int n,m,x,y,a[10][10],b[10][10];char c;
int cnt[10],testcase;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	while(cin>>n){
		testcase++;
		if(testcase!=1){
			cout<<endl;
			cout<<"**********************************"<<endl;
			cout<<endl;
		}
		cout<<"Problem #"<<testcase<<endl<<endl;;
		cin>>m;
		rp(i,n)cnt[i]=0;
		rp(i,n)rp(j,n)a[i][j]=b[i][j]=0;
		rp(i,m){
			cin>>c>>x>>y;
			if(c=='H')a[x][y]=1;
			else b[y][x]=1;
		}
		rp(x,n)rp(y,n)rep(len,1,n-x){
			int xx=x+len,yy=y+len;
			if(yy>n)continue;
			bool flag=1;
			rep(i,y,yy-1)flag&=a[x][i];
			rep(i,y,yy-1)flag&=a[xx][i];
			rep(i,x,xx-1)flag&=b[i][y];
			rep(i,x,xx-1)flag&=b[i][yy];
			if(flag){
				cnt[len]++;
			}
		}bool flag=0;
		rp(i,n)if(cnt[i]){
			cout<<cnt[i]<<" square (s) of size "<<i<<endl;
			flag=1;
		}if(!flag)cout<<"No completed squares can be found."<<endl;
	}
	return 0;
}
//Nyn-siyn-he

C

考虑枚举,对于 L 操作,我们枚举夹住的另一边棋子,然后从八个方向往外扩展,如果前面全都是另一种颜色,当前还是空的,那么就可以放置。然后直接输出即可。

对于 M 操作,我们从当前的棋子往八个方向暴力扩展并且记录。如果遇到相同棋子,就把前面记录的一列都更改。

然后暴力处理。

int a[10][10],cur,b[10][10];string s;
int dx[8]={1,-1,1,-1,1,-1,0,0};
int dy[8]={1,-1,-1,1,0,0,1,-1};
inline void List(){
	rp(i,8)rp(j,8)b[i][j]=0;
	rp(i,8)rp(j,8)if(a[i][j]==cur)rd(k,8){
		int cnt=0,x=i+dx[k],y=j+dy[k];
		while(x>0&&y>0&&x<9&&y<9){
			if(a[x][y]==(cur^1))cnt++;
			if(a[x][y]==cur)break;
			if(a[x][y]==-1){
				if(cnt)b[x][y]=1;
				break;
			}x+=dx[k],y+=dy[k];
		}
	}vt<pii>ans;
	rp(i,8)rp(j,8)if(b[i][j])ans.pb({i,j});
	if(!ans.size())cout<<"No legal move.";
	bool fft=0;
	for(auto i:ans){
		if(fft)cout<<' ';fft=1;
		cout<<"("<<i.first<<","<<i.second<<")";
	}cout<<endl;
}
inline bool Move(int i,int j){
	if(a[i][j]!=-1)return 0;
	bool f=0;
	rd(k,8){
		int cnt=0,x=i+dx[k],y=j+dy[k];
		vt<pii>v;
		while(x>0&&y>0&&x<9&&y<9){
			if(a[x][y]==(cur^1))v.pb({x,y});
			if(a[x][y]==cur){
				cnt=1;
				break;
			}if(a[x][y]==-1)break;
			x+=dx[k],y+=dy[k];
		}if(cnt)for(auto i:v)a[i.first][i.second]^=1,f=1;
	}if(!f)return 0;
	a[i][j]=cur,cur^=1;
	return 1;
}
inline void solve(){
	rep(i,0,9)rep(j,0,9)a[i][j]=-1;
	rp(i,8){
		cin>>s;
		rp(j,8){
			if(s[j-1]=='B')a[i][j]=1;
			if(s[j-1]=='W')a[i][j]=0;
		}
	}cin>>s;
	if(s[0]=='B')cur=1;
	else cur=0;
	while(s!="Q"){
		cin>>s;
		if(s[0]=='L')List();
		else if(s[0]=='M'){
			bool res=Move(s[1]-'0',s[2]-'0');
			if(!res){
				cur^=1;Move(s[1]-'0',s[2]-'0');
			}
			int cnt0=0,cnt1=0;
			rp(i,8)rp(j,8){
				if(a[i][j]==0)cnt0++;
				if(a[i][j]==1)cnt1++;
			}cout<<"Black - ";
			if(cnt1<10)cout<<' ';
			cout<<cnt1;
			cout<<" "<<"White - ";
			if(cnt0<10)cout<<' ';
			cout<<cnt0<<endl;
		}else{
			rp(i,8){
				rp(j,8){
					if(a[i][j]==-1)cout<<'-';
					else if(a[i][j]==1)cout<<'B';
					else cout<<'W';
				}cout<<endl;
			}
		}
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin>>t;
	rd(_,t){
		if(_)cout<<endl;
		solve();
	}
	return 0;
}
//Nyn-siyn-hert

D

发现只要我们满足三对面互相仍然相对,我们可以从三对面中每个各选一个,映射到 \(1,2,3\),然后就确定了这个摆放的方式。所以,枚举前三个面各自映射到什么即可。

string s;
int to[6],b[6];
inline void solve(){
	string t="";
	rep(i,6,(int)s.size()-1)t+=s[i];
	rd(i,6)to[i]=i;
	do{
		if(to[0]+to[1]==5||to[1]+to[2]==5||to[2]+to[0]==5)continue;
		rd(i,3)b[i]=to[i];
		rd(i,3)b[5-i]=5-to[i];
		string res="";
		rd(i,6)res+=s[b[i]];
		if(res==t){
			cout<<"TRUE"<<endl;
			return;
		}
	}while(next_permutation(to,to+6));
	cout<<"FALSE"<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	while(cin>>s){
		solve();
	}
	return 0;
}
//Nyn-siyn-hert

E

考虑二进制位从高往低枚举,如果所有串当前位置都相同就不变,否则两个串后面都是 \(0\),然后转成他要的格式输出。

int n;;
ui a[1005];
inline ui input(){
	string s;
	ui res=0,b;
	cin>>s;
	for(auto &i:s)if(i=='.')i=' ';
	stringstream ss(s);
	rd(i,4){
		ss>>b;
		res=res<<8|b;
	}return res;
}
inline void output(ui x){
	per(i,0,3){
		ui res=(x>>(8*i)&255);
		cout<<res;
		if(i!=0)cout<<'.';
	}cout<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	while(cin>>n){
		rp(i,n)a[i]=input();
		ui res=0,cur=0;
		per(i,0,31){
			ui cs=(a[1]>>i&1),fl=0;
			rep(j,2,n)if((a[j]>>i&1)!=cs)fl=1;
			if(fl){
				rep(j,0,i)cur<<=1,res<<=1;
				break;
			}else cur=(cur<<1|1),res=(res<<1|cs);
		}output(res);
		output(cur);
	}
	return 0;
}
//Nyn-siyn-hert

F

考虑把每个单词对应的编码搞到字典树上。然后在每个节点存储是否有别的单词,以及字典序最小的单词。然后如果查询的时候从字典树从上往下记录最近的单词。然后提前预处理子树中最近的单词以及深度,两者对比一下,选出最近的那个。

const int N=3200000;
map<char,string>mp;
string s,t;
int cnt=1,trie[N+5][2],fa[N+5],sz=1,vis[N+5],fr[N+5],dep[N+5],is[N+5];
string ans[N+5];
inline void add(string s,string cur){
	int cyr=1;
	for(auto i:s){
		int p=(i=='-');
		if(!trie[cyr][p])trie[cyr][p]=++sz,fa[sz]=cyr,dep[sz]=dep[cyr]+1;
		if(sz>N)exit(1);
		cyr=trie[cyr][p];
	}
	if(ans[cyr]!=""){
		is[cyr]=1;
		if(ans[cyr]>cur)ans[cyr]=cur;
	}else ans[cyr]=cur;
}
inline void qry(string s){
	int cyr=1;string lst="-1";int d=0;
	for(auto i:s){
		if(ans[cyr]!="")lst=ans[cyr],d=0;
		int p=(i=='-');
		if(!trie[cyr][p]){
			cout<<lst<<'?'<<endl;
			return;
		}cyr=trie[cyr][p],d++;
	}
	if(ans[cyr]==""){
		if(lst!="-1"&&dep[fr[cyr]]-dep[cyr]>=d)cout<<lst<<'?'<<endl;
		else cout<<ans[fr[cyr]]<<'?'<<endl;
	}else if(!is[cyr])cout<<ans[cyr]<<endl;
	else cout<<ans[cyr]<<'!'<<endl;
}
inline void init(){
	queue<int>q;
	rp(i,sz)if(ans[i].size()){
		fr[i]=i,vis[i]=1;
		q.push(i);
	}
	while(q.size()){
		int x=q.front();q.pop();
		if(fa[x]&&!vis[fa[x]]){
			fr[fa[x]]=fr[x],vis[fa[x]]=1;
			q.push(fa[x]);
		}
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>s;
	while(s!="*"){
		char c=s[0];
		cin>>s;mp[c]=s;
		cin>>s;
	}cin>>s;
	while(s!="*"){
		string t="";
		for(auto i:s)t+=mp[i];
		add(t,s);
		cin>>s;
	}init();
	cin>>s;
	while(s!="*"){
		qry(s);
		cin>>s;
	}cout<<endl;
	return 0;
}
//Nyn-siyn

G

考虑先还原出整个磁盘,直接每一位设置成校验码异或上其他的和,如果一行有两个或者没有且总和不对就不合法。

然后分块解决,每块先判断是否是校验块,然后依次加入。每加入四个就输出即可。

int d,s,b;
char c[6][6400],h;
int a[6][6400],p,test;
inline void output(int x){
	if(x>=10)cout<<(char)('A'+x-10);
	else cout<<x;
}
inline void solve(){
	p=(h=='O');
	rd(i,d)rd(j,s*b){
		cin>>c[i][j];
		if(c[i][j]=='x')a[i][j]=-1;
		else a[i][j]=c[i][j]-'0';
	}
	rd(j,s*b){
		int cnt=0,cur=p;
		rd(i,d){
			if(a[i][j]==-1)cnt++;
			else cur^=a[i][j];
		}if(cnt>1||(cnt==0&&cur)){
			test++;
			cout<<"Disk set "<<test<<" is invalid."<<endl;
			return;
		}rd(i,d)if(a[i][j]==-1)a[i][j]=cur;
	}test++;
	cout<<"Disk set "<<test<<" is valid, contents are: ";
	int res=0,cnt=0;
	rd(j,b)rd(i,d){
		if(j%d==i)continue;
		rep(x,j*s,j*s+s-1){
			res=(res<<1|a[i][x]),cnt++;
			if(cnt==4)output(res),res=0,cnt=0;
		}
	}
	if(cnt){
		while(cnt<4)res<<=1,cnt++;
		output(res);
	}
	cout<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>d;
	while(d){
		cin>>s>>b>>h;
		solve();cin>>d;
	}
	return 0;
}
//Nyn-siyn

H

考虑直接模拟,然后循环很多次。我们发现,可用的状态数量有限,不太会超过 \(10^5\) 这个级别,所以直接模拟 \(10^5\) 次,找不到就 \(-1\)

int n,a[11],b[11],c[11],test;
inline void solve(){
	rp(i,n)cin>>a[i]>>b[i]>>c[i];
	rep(i,1,1000000){
		int A=0,B=0;
		rp(j,n){
			if(c[j]>a[j])A++;
			else B++;
		}
		if(!A){
			cout<<"Case "<<test<<": "<<i<<endl;
			return;
		}
		rp(j,n)c[j]++;
		rp(j,n){
			if(c[j]==a[j]+b[j]+1)c[j]=1;
			else if(c[j]==a[j]+1){
				if(A<=B)c[j]=1;
			}
		}
	}cout<<"Case "<<test<<": "<<-1<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	while(n){
		test++;
		solve();
		cin>>n;
	}
	return 0;
}
//Nyn-siyn

I

首先,理解题意是难点。对于一个数组 \(P(i)\),每个位置是 \(p\) 个比特,我们定义偏移量为某个位置到数组起点的字节数量。也就是 \(下标\times p\)

然后,我们对于一个固定的数组 \(P(i)\),本来我们是把 \(Q(i)\) 效率最大化的,也就是每 \(q\) 个比特分一段。但是现在我们想要通过整数 \(A\)\(B\) 重置 \(Q(i)\),使得 \(Q\) 的第 \(i\) 个位置距离开头 \((Po(i)+Po(i)<<A)>>B\),其中 \(Po\)\(P\) 中的第 \(i\) 个整数距离开头的字节数。

这样,我们要在满足 \(Q(i)\) 中每个信息都能被放下的前提,最小化 \(Q(i)\) 的总字节数。

考虑暴力枚举 \(A,B\),然后要检查当前 \(A,B\) 是否满足条件。

我们发现,设 \(Ql(i)\) 表示位置 \(i\) 的长度,\(Ql(i)\) 其实就是 \(\lfloor \dfrac{Po(i)(2^A+1)}{2^B}\rfloor-\lfloor \dfrac{Po(i-1)(2^A+1)}{2^B}\rfloor\)。那么设 \(\dfrac{p(2^A+1)}{2^B}=k\)\(Qo(i)=\lfloor ik\rfloor-\lfloor (i-1)k\rfloor\)

这个是什么意思呢?然后我们发现 \(Qo(i)\ge Qo(1)\)。为什么呢?因为 \(Qo(1)=\lfloor k\rfloor\),然后如果 \(Qo(i)<Qo(1)\)\(\lfloor k\rfloor+\lfloor (i-1)k\rfloor>\lfloor ik\rfloor\)。但是这是不可能发生的,因为 \(a\)\(b\) 的小数部分之和一定大于等于 \(a+b\) 的小数部分。

所以,只要满足 \(\lfloor \dfrac{p(2^A+1)}{2^B}\rfloor\ge q\),当前的 \(A\)\(B\) 即为合法。

然后是第 \(n\) 个位置的偏移量,但是我们发现,我们不需要计算 \(n+1\) 位置的偏移量,因为我们只要让第 \(n\) 个位置的长度为 \(q\) 即可,不需要为了后面的下标满足条件而延长长度。所以,算出 \(K=\lfloor \dfrac{(n-1)p(2^A+1)}{2^B}\rfloor+q\)

也就完成了题目。

ll n,p,q,sp[1050005];
ll mnk=1e18,mna,mnb;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	while(cin>>n>>p>>q){
		mnk=1e18;
		rep(a,0,32)rep(b,0,32){
			ll x=(p+(p<<a))>>b,k=((n-1)*p+(((n-1)*p)<<a))>>b;
			if(x>=q&&k+q<mnk)
				mnk=k+q,mna=a,mnb=b;
		}cout<<mnk<<" "<<mna<<" "<<mnb<<endl;
	}
	return 0;
}
//Nyn-siyn-hert

J

考虑二分答案。然后判断海平面为 \(x\) 的时候总体积是多少。实数二分固定二分总次数即可。计算出最小的能让总体积大于等于预定值的 \(x\),输出答案即可。

const ld eps=1e-8;
ll n,m,h[31][31],g,test=0;
inline void solve(){
	rp(i,n)rp(j,m)cin>>h[i][j];
	ld l=-1e16,r=1e16,mid,ans=0;
	cin>>g;
	rd(_,200){
		mid=(l+r)/2;
		ld tot=0;bool flag=0;
		rp(i,n)rp(j,m){
			if(mid>h[i][j]+eps){
				tot+=100*(mid-h[i][j]);
				if(tot>g+eps){
					flag=1,i=n,j=m;
				}
			}
		}if(flag)ans=mid,r=mid;
		else l=mid;
	}ld res=0;
	rp(i,n)rp(j,m)if(ans>h[i][j]+eps)res+=100;
	res/=(n*m);
	cout<<"Region "<<test<<endl;cout<<fixed<<setprecision(2);
	cout<<"Water level is "<<ans<<" meters."<<endl;
	cout<<res<<" percent of the region is under water."<<endl<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	while(n){
		test++;
		solve();
		cin>>n>>m;
	}
	return 0;
}
//Nyn-siyn-hert

K

考虑平均数是 \(a\),我们其实就是要若干个 \(a\) 和若干个 \(a+1\),使得总和是 \(sum\)

然后考虑 \(dp_{i,j}\) 表示前 \(i\) 个数,其中有 \(j\) 个是 \(a+1\) 的最小流量。\(i\)\(i+1\) 之间的流量就是 \(i-j\)\(a\)\(j\)\(a+1\) 的和(也就是最终的前 \(i\) 位和)和最早的前 \(i\) 位和的差的绝对值。

然后就 \(dp\) 解决。

ll n,a[5005],dp[5005][5005];
ll s[5005];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	rp(i,n)cin>>a[i];
	rp(i,n)a[i]+=1000000000;
	rp(i,n)s[i]=s[i-1]+a[i];
	ll ap=s[n]/n,lf=s[n]%n;
	dp[0][0]=0;
	rep(i,1,n)rep(j,0,n)dp[i][j]=1e18;
	rep(i,0,n-1)rep(j,0,i)if(dp[i][j]!=1e18){
		ll d=abs((i+1)*ap+j+1-s[i+1]);
		dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]+d);
		d=abs((i+1)*ap+j-s[i+1]);
		dp[i+1][j]=min(dp[i+1][j],dp[i][j]+d);
	}cout<<dp[n][lf]<<endl;
	return 0;
}
//Nyn-siyn-hert

L

考虑线段树维护区间加,区间和。

ll n,q,a[200005],l,r,v;
char c;
struct node{
	ll l,r,tg,s;
}sg[800005];
inline void init(int i,int l,int r){
	sg[i].l=l,sg[i].r=r,sg[i].tg=0,sg[i].s=0;
	if(l==r)return(void)(sg[i].s=a[l]);
	init(i<<1,l,(l+r)>>1);
	init(i<<1|1,((l+r)>>1)+1,r);
	sg[i].s=sg[i<<1].s+sg[i<<1|1].s;
}
inline void push(int i){
	if(!sg[i].tg)return;
	sg[i].s+=(sg[i].r-sg[i].l+1)*sg[i].tg;
	if(sg[i].l!=sg[i].r){
		sg[i<<1].tg+=sg[i].tg;
		sg[i<<1|1].tg+=sg[i].tg;
	}sg[i].tg=0;
}
inline void add(int i,int l,int r,ll v){
	push(i);
	if(sg[i].l>r||sg[i].r<l)return;
	if(sg[i].l>=l&&sg[i].r<=r){
		sg[i].tg+=v;
		return push(i);
	}add(i<<1,l,r,v);
	add(i<<1|1,l,r,v);
	sg[i].s=sg[i<<1].s+sg[i<<1|1].s;
}
inline ll qry(int i,int l,int r){
	push(i);
	if(sg[i].l>r||sg[i].r<l)return 0;
	if(sg[i].l>=l&&sg[i].r<=r)return sg[i].s;
	ll res=qry(i<<1,l,r)+qry(i<<1|1,l,r);
	sg[i].s=sg[i<<1].s+sg[i<<1|1].s;
	return res;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>q;
	rp(i,n)cin>>a[i];
	init(1,1,n);
	rp(i,q){
		cin>>c>>l>>r;
		if(c=='C'){
			cin>>v;
			add(1,l,r,v);
		}else{
			cout<<qry(1,l,r)<<endl;
		}
	}
	return 0;
}
//Nyn-siyn-hert
posted @ 2023-06-25 19:27  jucason_xu  阅读(8)  评论(0编辑  收藏  举报