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