[IOI2018]-day1 簡要題解
因為只有4種字母,且只能詢問次,那麼我們需要平均一次就要確定一位。
而詢問的串長,最長為,所以每次對於一個要確定的字母我們可以直接輸出四種情況。但是這樣需要2次確定一位,所以我們需要進一步優化。
首先,我們可以花兩步確定第一位:
詢問"AB",如果返回的值大於0,則就是"AB"中的一個,然後就輸出一個"A",如果返回值為1,那麼開頭就是"A",否則為"B"。如果不是"AB",那麼就是"XY",所以輸出"X",和"AB"同樣的判斷方法,那麼就可以知道第一位。
對於後面的每一位,我們要儘量做到一次詢問確定一位。由於開頭的那一位不會在後面再次出現,那麼我們只剩下三種選擇。
舉個例子說明:
假如開頭是"A",那麼下一位可能為"B,X,Y",而如果每次只增加一位的話,就還要花費一個操作去確定,所以我們增加兩位:“BB”,“BX”,“BY”,這樣如果返回值沒有增加,下一位就不是"B",但是如果增加了兩個,那麼下一位就是"B",如果不是"B"的話,還要判斷"X,Y",由於"A"不能再出現,所以我們再加一個"XA",這樣如果返回值只增加了一個,那麼下一位就是"X",否則沒增加就是"Y"。
所以直接模擬,而到了最後一位,直接按照開始的方法,在剩下三種情況中花費兩個操作判斷一下即可。
程式碼(互動):
#include "combo.h"
using namespace std;
string Nex(char a,int id){
if(a=='A'){
if(id==1) return "BB";
if(id==2) return "BX";
if(id==3) return "BY";
if(id==4) return "XA";
}else if(a=='B'){
if(id==1) return "AA";
if(id==2) return "AX";
if(id==3) return "AY";
if(id==4) return "XB";
}else if(a=='X'){
if(id==1) return "AA";
if(id==2) return "AB";
if(id==3) return "AY";
if(id==4) return "BX";
}else if(a=='Y'){
if(id==1) return "AA";
if(id==2) return "AB";
if(id==3) return "AX";
if(id==4) return "BY";
}
}
string Ok(char a,int con,int bef){
if(a=='A'){
if(con==bef){
return "Y";
}else if(con==bef+1){
return "X";
}else if(con==bef+2){
return "B";
}
}else if(a=='B'){
if(con==bef){
return "Y";
}else if(con==bef+1){
return "X";
}else if(con==bef+2){
return "A";
}
}else if(a=='X'){
if(con==bef){
return "Y";
}else if(con==bef+1){
return "B";
}else if(con==bef+2){
return "A";
}
}else if(a=='Y'){
if(con==bef){
return "X";
}else if(con==bef+1){
return "B";
}else if(con==bef+2){
return "A";
}
}
}
string End(char a,int id){
if(a=='A'){
if(id==1){
return "B";
}else if(id==2){
return "X";
}else if(id==3){
return "Y";
}
}else if(a=='B'){
if(id==1){
return "A";
}else if(id==2){
return "X";
}else if(id==3){
return "Y";
}
}else if(a=='X'){
if(id==1){
return "A";
}else if(id==2){
return "B";
}else if(id==3){
return "Y";
}
}else if(a=='Y'){
if(id==1){
return "A";
}else if(id==2){
return "B";
}else if(id==3){
return "X";
}
}
}
string guess_sequence(int N){
string st="";
string pre="AB";
int con=press(pre);
if(!con){
con=press("X");
if(con) st="X";
else st="Y";
}else{
con=press("A");
if(con) st="A";
else st="B";
}
pre=st;
if(N==1) return pre;
for(int i=2;i<=N-1;i++){
string ask="";
for(int j=1;j<=4;j++)ask+=pre+Nex(st[0],j);
con=press(ask);
pre+=Ok(st[0],con,i-1);
}
string last=pre+End(st[0],1)+pre+End(st[0],2);
con=press(last);
if(con==N-1){
last=pre+End(st[0],3);
}else{
last=pre+End(st[0],1);
con=press(last);
if(con==N-1){
last=pre+End(st[0],2);
}
}
return last;
}
開始我想的是,對於一個字首,我們維護裡面最左下角的點和最右上角的點,然後每次詢問我們就判斷每個字首數的個數是不是等於所在矩形區間的大小,為了支援快速查詢修改,外面再套一個樹,或者用主席樹之類的,但是就是十分複雜,且複雜度為的,是無法通過所有資料的。
所以我們轉換思路,對於一個字首如果它形成了一個矩形,那麼與其相交的所有的子矩形要麼是隻有兩個相交,要麼是全部在裡面,要麼是隻有一個相交,而一個的只有矩形的四個頂點,而三個相交的又不存在,但是2,4個的個數不確定,所以我們就只維護對於一個字首的所在格子形成的圖形與其相交的所有的子矩形中的個數,如果合法的話必須只有個。
這個可以先預處理出來,然後用權值線段樹維護即可,每次修改就交換然後線段樹上改這兩個點周圍包含它的的子矩形即可,複雜度。
#include "seats.h"
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e6+10;
vector <int> mp[M];
int rr[M],cc[M];
int B1[M],B3[M];
int n,m;
struct node{
int b1,b3;
node(){}
node(int a,int b):b1(a),b3(b){}
bool operator <(const node &a)const{
return b1<a.b1||(b1==a.b1&&b3<a.b3);
}
node operator +(const node &a)const{
return node(b1+a.b1,b3+a.b3);
}
bool operator ==(const node &a)const{
return b1==a.b1&&b3==a.b3;
}
node operator +=(const node &a){return *this=*this+a;}
bool isok(){return b1==4&&!b3;}
};
int tot,root;
node S[M<<2],lazy[M<<2];
int ls[M<<2],rs[M<<2],sum[M<<2];
void pushup(int o){
S[o]=min(S[ls[o]],S[rs[o]]);sum[o]=0;
if(S[o]==S[ls[o]])sum[o]+=sum[ls[o]];
if(S[o]==S[rs[o]])sum[o]+=sum[rs[o]];
}
void pushdown(int o){
node &now=lazy[o];
if(now==node(0,0)) return;
S[ls[o]]+=now;S[rs[o]]+=now;
lazy[ls[o]]+=now;lazy[rs[o]]+=now;
now=node(0,0);
}
void build(int &o,int l,int r){
o=++tot;lazy[o]=node(0,0);
if(l==r){
S[o]=node(B1[l],B3[l]);sum[o]=1;
return;
}
int mid=l+r>>1;
build(ls[o],l,mid);
build(rs[o],mid+1,r);
pushup(o);
}
void update(int o,int l,int r,int L,int R,node v){
if(L>R) return;
if(L<=l&&r<=R){
S[o]+=v;lazy[o]+=v;
return;
}
pushdown(o);
int mid=l+r>>1;
if(L<=mid) update(ls[o],l,mid,L,R,v);
if(R>mid) update(rs[o],mid+1,r,L,R,v);
pushup(o);
}
int query(){
if(S[root].isok()) return sum[root];
else return 0;
}
int stk[4];
const int dx[]={0,1,0,1},dy[]={0,0,1,1};
void init(int x,int y){
for(int i=0;i<=3;i++){
stk[i]=mp[x+dx[i]][y+dy[i]];
} sort(stk,stk+4);
}
void give_initial_chart(int H, int W, std::vector<int> R, std::vector<int> C) {
n=H,m=W;
for(int i=0;i<=n+1;i++){
mp[i].resize(m+2);
for(int j=0;j<=m+1;j++){
mp[i][j]=n*m+1;
}
}
for(int i=1,sz=n*m;i<=sz;i++){
rr[i]=R[i-1]+1;cc[i]=C[i-1]+1;
mp[rr[i]][cc[i]]=i;
}
for(int i=0;i<=n;i++)for(int j=0;j<=m;j++){
init(i,j);
++B1[stk[0]];--B1[stk[1]];
++B3[stk[2]];--B3[stk[3]];
}
for(int i=1,sz=n*m;i<=sz;i++){
B1[i]+=B1[i-1];
B3[i]+=B3[i-1];
}
build(root,1,n*m);
}
void New(int x,int y,int v){
init(x,y);
update(root,1,n*m,stk[0],stk[1]-1,node(v,0));
update(root,1,n*m,stk[2],stk[3]-1,node(0,v));
}
const int tx[]={0,-1,0,-1},ty[]={0,0,-1,-1};
void Up(int x,int y,int v){
for(int i=0;i<=3;i++)
New(x+tx[i],y+ty[i],-1);
mp[x][y]=v;
for(int i=0;i<=3;i++)
New(x+tx[i],y+ty[i],1);
}
int swap_seats(int a, int b) {
++a;++b;
int v1=mp[rr[a]][cc[a]],v2=mp[rr[b]][cc[b]];
swap(rr[a],rr[b]);swap(cc[a],cc[b]);
Up(rr[a],cc[a],v1);
Up(rr[b],cc[b],v2);
return query();
}
由於每次每種形態不能去和能去的都是一個編號的字首或者字尾,所以我們考慮將圖轉換成兩個重構樹,一個是編號小的向大的,另一個是編號大的向小的。
然後一個字首就對應了小的向大的樹上的一個子樹,字尾就對應了大的向小的上面的一個子樹,每次用類似二維數點的方式判斷這兩個子樹集合是否有交,有交就證明可以在這裡變成狼人到達終點,否則就不行。
將詢問離線,在兩個樹上的dfs序上,就可以用樹狀陣列來求答案啦。
這裡的重構樹的方法類似於kruskal重構樹,所以用個並查集來建就好了。
#include "werewolf.h"
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(a) ((a)&(-(a)))
using namespace std;
const int M=1e6+1;
const int Log=20;
struct DSU{
int f[M];
void init(int n){
for(int i=0;i<=n;i++)f[i]=i;
}
int find(int a){return f[a]==a?a:f[a]=find(f[a]);}
int & operator [](int a){return f[a];}
int operator ()(int a){return find(a);}
};
int n;
struct Rebuild_Tree{
vector <int> g[M],t[M];
int in[M],root;
void add(int a,int b){g[a].push_back(b);}
void link(int a,int b){t[a].push_back(b);++in[b];}
DSU F;
int dfn[M],out[M],tim;
int f[Log][M];
void dfs(int a){
dfn[a]=++tim;
for(int i=1;i<Log;i++)
f[i][a]=f[i-1][f[i-1][a]];
for(auto v: t[a]){
f[0][v]=a;
dfs(v);
}
out[a]=tim;
}
void build(bool flag){
F.init(n);
if(flag){
for(int i=1,u;i<=n;i++){
for(auto v: g[i]){
u=F(v);
if(i!=u){
F[u]=i;
link(i,u);
}
}
}
}else{
for(int i=n,u;i>=1;i--){
for(auto v: g[i]){
u=F(v);
if(i!=u){
F[u]=i;
link(i,u);
}
}
}
}
for(int i=1;i<=n;i++)if(!in[i]){root=i;break;}
dfs(root);
}
void Find(int a,int to,int tp,int &x,int &y){
for(int i=Log-1;i>=0;i--){
if(f[i][a]&&(tp?f[i][a]>=to:f[i][a]<=to)){
a=f[i][a];
}
}
x=dfn[a]-1;
y=out[a];
}
}T1,T2;
int bit[M];
void add(int a,int b){
for(;a<=n;a+=lowbit(a))bit[a]+=b;
}
int query(int a){
int res=0;
for(;a;a-=lowbit(a))res+=bit[a];
return res;
}
struct Ask{
int s,t;
Ask(){}
Ask(int a,int b):s(a),t(b){}
};
vector <Ask> Q[M];
vector <int> ans;
int ref[M];
int a,b;
vector<int> check_validity(int N,vector<int> X,vector<int> Y,vector<int> S,vector<int> E,vector<int> L,vector<int> R) {
n=N;
for(int i=0,sz=X.size();i<sz;i++){
a=X[i]+1;b=Y[i]+1;
if(a<b)swap(a,b);
T1.add(a,b);T2.add(b,a);
}
T1.build(1);
T2.build(0);
for(int i=0,sz=S.size(),lx,rx,ly,ry;i<sz;i++){
T2.Find(S[i]+1,L[i]+1,1,lx,rx);
T1.Find(E[i]+1,R[i]+1,0,ly,ry);
Q[lx].push_back(Ask(ly,i));Q[lx].push_back(Ask(-ry,i));
Q[rx].push_back(Ask(-ly,i));Q[rx].push_back(Ask(ry,i));
}
for(int i=1;i<=n;i++)
ref[T2.dfn[i]]=T1.dfn[i];
ans.resize(S.size());
for(int i=1;i<=n;i++){
add(ref[i],1);
for(int j=0,sz=Q[i].size();j<sz;j++){
Ask now=Q[i][j];
int fl=now.s<0?now.s=-now.s,-1:1;
ans[now.t]+=query(now.s)*fl;
}
}
for(int i=0,sz=ans.size();i<sz;i++)if(ans[i]>0)ans[i]=1;else ans[i]=0;
return ans;
}
相關文章
- ABC366簡要題解
- ARC180 部分簡要題解
- HDU100題簡要題解(2080~2089)
- HNOI2019 DAY1 題解
- 省選聯考 2024 簡要題解
- gym102536部分簡要題解
- 國慶day1補題
- Offer68題 Day1
- Day1 最短路專題
- 【CH Round #48 - Streaming #3(NOIP模擬賽Day1)】 題解
- LeetCode刷題記錄——day1LeetCode
- 【刷題打卡】day1 - 字串string字串
- Spring高階註解-Day1Spring
- P4898 [IOI2018] seats 排座位
- P6156 簡單題 題解
- Cmake簡要配置
- day1
- 深拷貝和淺拷貝的簡要詳解
- CF#488簡單題解
- 磁碟原理簡要分析
- SDRAM簡要歷史
- Python Day1Python
- 2019年4月程式語言TIOBE指數簡要解讀
- 賽題要點
- TotallySAF+Ubuntu簡要配置Ubuntu
- PHP 魔術常量簡要PHP
- 簡要理解CommonJS規範JS
- 前端入門——day1(簡介及推薦書籍和網站)前端網站
- Learning Java day1Java
- Laravel 框架 day1Laravel框架
- 怎樣解題|題9.2.21:化簡八次根式
- 題解1249:簡單編碼 (Java描述)Java
- Spring入門筆記簡要Spring筆記
- Oracle RAC簡介與要點Oracle
- 數論內容簡要整理
- Multidex(分包)實現簡要分析IDE
- 11.4 - ? 改題紀要
- 2024.4 做題紀要