Noip模擬97 2021.11.14
T1 構造字串
看出是圖論,但是沒有試著用冰茶姬,然後特判\(-1\)的還多了,就直接掛成\(40pts\)了
然後別人分數都很高就直接底墊墊了。。。。對,又一次底墊墊了
轉化題意很簡單,要求構造一串字典序最小的序列滿足一些數相等,一些數不相等
那麼直接分情況討論,先把要求相等的數按照關係分成一堆聯通塊,然後每一條不相等的關係當做一條邊將聯通塊連起來
不合法的判斷只有一條,並沒有我考場上想的那麼麻煩,直接就如果聯通塊內有連邊就是不合法
然後建完圖之後就是變為了只有不相等的部分分(\(z_i=0\))的情況,那麼直接貪心的按照編號從小往大掃,每次開個桶找個\(mex\)即可
考場上的\(SB\)做法就是因為沒有使用冰茶姬合併相同的關係,而是把相同的和不同的放在了一起處理,這樣會導致錯誤,\(Hack\)
說一下剛開始的時候想的假做法,沒有保證字典序最小的時候可以對每一條關係整一個邊權,不相等的關係邊權為\(1\),相等的為\(0\),然後每個聯通塊跑一遍\(dfs\)就行了
str
#include<bits/stdc++.h> #define int long long using namespace std;FILE *wsn; auto read=[](){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; }; const int NN=2005; int n,m,ans[NN],bin[NN]; bool vis[NN]; struct node{ int a,b,c; bool operator<(const node&x)const{ return c>x.c; } }op[NN]; vector<int>S[NN]; namespace DSU{ int fa[NN]; inline int getfa(int x){return fa[x]=((fa[x]==x)?x:getfa(fa[x]));} inline void merge(int x,int y){ x=getfa(x); y=getfa(y); if(x==y) return;if(x>y) swap(x,y); fa[y]=x; } }using namespace DSU; namespace WSN{ inline short main(){ wsn=freopen("str.in","r",stdin); wsn=freopen("str.out","w",stdout); n=read(); m=read(); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1,a,b,c;i<=m;i++){ a=read(),b=read(),c=read(); if(a>b) swap(a,b);op[i]=node{a,b,c}; } sort(op+1,op+m+1); for(int i=1;i<=m&&op[i].c!=0;i++){ int a=op[i].a,b=op[i].b,c=op[i].c; for(int j=1,x,y;j<=c;j++) x=a+j-1,y=b+j-1,merge(x,y); if(b+c<=n) op[++m]=node{a+c,b+c,0}; } for(int i=m;i>0&&op[i].c==0;i--){ int a=op[i].a,b=op[i].b,c=op[i].c; if(getfa(a)==getfa(b))return puts("-1"),0; S[getfa(a)].push_back(getfa(b)); S[getfa(b)].push_back(getfa(a)); } for(int i=1;i<=n;i++)if(!vis[getfa(i)]){ int mex=0,x=getfa(i); vis[x]=1; memset(bin,0,sizeof(bin)); for(auto y:S[x])if(y<x) ++bin[ans[y]]; while(bin[mex]) ++mex; ans[x]=mex; } for(int i=1;i<=n;i++)if(getfa(i)!=i)ans[i]=ans[getfa(i)]; for(int i=1;i<=n;i++)printf("%lld ",ans[i]); puts(""); return 0; } } signed main(){return WSN::main();}
\(\color{white}{過於之菜了,已經連著好幾天沒有切題了,而且還都不是不會,就是打不對,很鬱悶。。。。}\)
T2 尋寶
非常無腦的\(bfs+dfs\),切忌把帶著陣列的判斷條件放在判斷下標的前面,否則很可能\(RE\),比如
然後這種送分題都沒能切,於是直接底墊墊。。。。。
然後我的打法比較難調,或者說我打的時候比較慌,出了很多zz錯誤,然後調了好久。。。後面的部分分沒來及打,不打掛的話應該能拿\(50\)左右的樣子。。。
剩下的思路就不說了,學過信隊的,寫過最短路的都可以輕鬆打過(除了我)
treasure
#include<set> #include<map> #include<queue> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define int long long using namespace std;FILE *wsn; int mzs; #define scanf mzs=scanf auto read=[](){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; }; const int NN=50005; int g[NN],n,m,k,q,in[NN],tot; char ch[NN]; auto id=[](int x,int y){return (x-1)*m+y;}; struct door{int x1,y1,x2,y2;}d[105]; typedef pair<int,int> PII; #define fi first #define se second #define mpr make_pair queue<PII> Q; map<int,PII>mp; set<int> jump[NN]; bitset<NN>ee[NN]; int dx[5]={0,1,-1,0,0}, dy[5]={0,0,0,1,-1}; bool vis[NN]; inline void walk(int x,int y){ Q.push(mpr(x,y)); vis[id(x,y)]=1; in[id(x,y)]=++tot; while(!Q.empty()){ int x=Q.front().fi,y=Q.front().se; Q.pop(); for(int i=1;i<=4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>0&&yy>0&&xx<=n&&yy<=m&&!vis[id(xx,yy)]&&!g[id(xx,yy)]){ Q.push(mpr(xx,yy)); vis[id(xx,yy)]=1; in[id(xx,yy)]=tot; } } } } inline void dfs(int x){ vis[x]=1; for(auto y:jump[x]){ if(vis[y]) continue; dfs(y); ee[x]|=ee[y]; } } namespace WSN{ inline short main(){ wsn=freopen("treasure.in","r",stdin); wsn=freopen("treasure.out","w",stdout); n=read(); m=read(); k=read(); q=read(); for(int i=1;i<=n;i++){ scanf("%s",ch+1); for(int j=1;j<=m;j++) g[id(i,j)]=(ch[j]=='#'); } for(int i=1;i<=k;i++){ int x1=read(),y1=read(),x2=read(),y2=read(); d[i]=door{x1,y1,x2,y2};mp[id(x1,y1)]=mpr(x2,y2); } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(!vis[id(i,j)]&&!g[id(i,j)]){ walk(i,j); } } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(mp.find(id(i,j))!=mp.end()){ PII now=mp[id(i,j)]; jump[in[id(i,j)]].insert(in[id(now.fi,now.se)]); ee[in[id(i,j)]][in[id(now.fi,now.se)]]=1; } } } for(int i=1;i<=tot;i++){ memset(vis,0,sizeof(vis)); dfs(i); } for(int i=1;i<=q;i++){ int x1=read(),y1=read(),x2=read(),y2=read(); int i1=in[id(x1,y1)],i2=in[id(x2,y2)]; if(i1==i2){puts("1");continue;} if(ee[i1][i2]){puts("1");} else puts("0"); } return 0; } } signed main(){return WSN::main();}
T3 序列
李超線段樹維護線段
考慮把過\(p\)區間分成右端點在\(p\)的一段最優區間加上左端點在\(p+1\)的一段最優區間
那麼每次對答案的統計就變成了固定一個點,找另一個點,這樣預計得到\(50\)
那麼嘗試化簡式子
設\(sa_i=\sum_{j=1}^i a_j\),\(sb_i=\sum_{j=1}^ib_j\)
最優即\(\max_{1\leq l\leq r}\{(sa_{r}-sa_{l-1})-k(sb_{r}-sb_{l-1})\}\)
即\(sa_r-k\cdot sb_{r}-\min_{0\leq l<r}\{ksb_{l}-sa_l\}\),離線之後李超樹維護直線即可
時間複雜度為\(O(n\log n)\),常數略大,空間複雜度為\(O(n)\)
seq
#include<bits/stdc++.h>
#define int long long
using namespace std; FILE *wsn;
auto read=[](){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
};
const int NN=2e6+5,inf=1e18;
const int lim=1e6;
int n,m,a[NN],b[NN],ans[NN],lst;
int sufa[NN],sufb[NN],prea[NN],preb[NN];
struct Query{
int p,k,id;
bool operator<(const Query&x)const{
return p==x.p?k<x.k:p<x.p;
}
}qu[NN];
namespace segment_tree{
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)>>1)
struct seg{
int k,b;
seg(){k=lim;b=inf;}
seg(int k,int b):k(k),b(b){}
int calc(int x){return k*x+b;}
}tr[NN<<2];
inline void update(seg x,int id=1,int l=-lim,int r=lim){
if(tr[id].calc(mid)>x.calc(mid)) swap(tr[id],x);
if(tr[id].calc(l)>x.calc(l))update(x,lid,l,mid);
if(tr[id].calc(r)>x.calc(r))update(x,rid,mid+1,r);
}
inline int query(int pos,int id=1,int l=-lim,int r=lim){
if(l>pos||r<pos) return inf;
if(l==r) return tr[id].calc(pos);
return min((mid<pos?query(pos,rid,mid+1,r):query(pos,lid,l,mid)),tr[id].calc(pos));
}
}using namespace segment_tree;
namespace WSN{
inline short main(){
wsn=freopen("seq.in","r",stdin);
wsn=freopen("seq.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
for(int i=1;i<=n;i++)prea[i]=prea[i-1]+a[i],preb[i]=preb[i-1]+b[i];
for(int i=n;i;i--)sufa[i]=sufa[i+1]+a[i],sufb[i]=sufb[i+1]+b[i];
for(int o=1,p,k;o<=m;o++) p=read(),k=read(),qu[o]=Query{p,k,o};
sort(qu+1,qu+m+1);
lst=0;
for(int i=1;i<=m;i++){
Query x=qu[i];
for(int j=lst;j<x.p;j++)
update(seg{-preb[j],prea[j]});
ans[x.id]=prea[x.p]-x.k*preb[x.p]-query(x.k);
lst=x.p;
}
memset(tr,0,sizeof(tr));
lst=n+1;
for(int i=m;i;i--){
Query x=qu[i];
for(int j=lst;j>=x.p+1;j--)
update(seg{-sufb[j],sufa[j]});
ans[x.id]+=sufa[x.p+1]-x.k*sufb[x.p+1]-query(x.k);
lst=x.p+1;
}
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
return 0;
}
}
signed main(){return WSN::main();}
T4 構樹
正在改。。。咕咕咕