3.26省選模擬+NOI-ONLINE
阿新 • • 發佈:2022-03-26
今日趣聞:
這三個人都是同機房的,卡最優解(大常數選手不參與)....以至於最優解第一頁都是我們機房的(有圖為證,共三人)
$NOI\ online$
$T1$
首先模擬一遍記錄這個點當前單調棧前面位置,然後線段樹(主席樹)查詢多少下標小於某個數的
#include<bits/stdc++.h> #define MAXN 500005 using namespace std; struct Tree { int ls,rs,sum; }tr[MAXN<<5]; struct node { int a,b; }poz[MAXN]; int n,q,tot,top,rt[MAXN],sta[MAXN],wei[MAXN];void Init() { top=0; for(int i=1;i<=n;i++) { while(top&&(poz[sta[top]].a==poz[i].a||poz[sta[top]].b<=poz[i].b)) top--; wei[i]=sta[top]; sta[++top]=i; } } void build(int &now,int l,int r) { if(!now) now=++tot; if(l==r) return; int mid=(l+r)>>1; build(tr[now].ls,l,mid); build(tr[now].rs,mid+1,r); } void Merge(int &rt1,int rt2,int l,int r,int poz) { rt1=++tot; if(l==r) { tr[rt1].sum=tr[rt2].sum+1; return ; } int mid=(l+r)>>1; if(poz<=mid) { tr[rt1].rs=tr[rt2].rs; Merge(tr[rt1].ls,tr[rt2].ls,l,mid,poz); } else { tr[rt1].ls=tr[rt2].ls; Merge(tr[rt1].rs,tr[rt2].rs,mid+1,r,poz); } tr[rt1].sum=(tr[tr[rt1].ls].sum+tr[tr[rt1].rs].sum); } int query(int rt1,int rt2,int l,int r,int k) { if(l==r) { return tr[rt2].sum-tr[rt1].sum; } int mid=(l+r)>>1; if(k<=mid) return query(tr[rt1].ls,tr[rt2].ls,l,mid,k); else return (tr[tr[rt2].ls].sum-tr[tr[rt1].ls].sum)+query(tr[rt1].rs,tr[rt2].rs,mid+1,r,k); } int main() { scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",&poz[i].a); for(int i=1;i<=n;i++) scanf("%d",&poz[i].b); Init(); build(rt[0],0,n); for(int i=1;i<=n;i++) { Merge(rt[i],rt[i-1],0,n,wei[i]); } for(int i=1,res,l,r;i<=q;i++) { scanf("%d%d",&l,&r); printf("%d\n",query(rt[l-1],rt[r],0,n,wei[l])); } }
$T2$
隨機化吧,看種子唄(會正解的$dalao$們能不能私信教教我啊)
#include<bits/stdc++.h> #define MAXN 1000005 using namespace std; bool vis[MAXN]; vector<int>peo[MAXN]; int pf; void sol() { int n; scanf("%d",&n); int Tim=n*20; for(int i=1;i<=n;i++) peo[i].clear(); for(int i=1,num,poz;i<=n;i++) { scanf("%d",&num); for(int j=1;j<=num;j++) { scanf("%d",&poz); peo[i].push_back(poz); } } srand(time(0)); int cnt; bool flag=false; while(Tim--) { int p1=rand()%n+1,p2=rand()%n+1; if(peo[p1].size()>peo[p2].size()) swap(p1,p2); for(int i=0;i<peo[p1].size();i++) { vis[peo[p1][i]]=true; } cnt=0; for(int i=0;i<peo[p2].size();i++) { cnt+=(vis[peo[p2][i]]?1:0); } for(int i=0;i<peo[p1].size();i++) { vis[peo[p1][i]]=false; } if(cnt==peo[p1].size()) continue; if(cnt==0) continue; puts("YES"); printf("%d %d\n",p1,p2); flag=true; break; } if(!flag) { puts("NO"); } } int T; int main() { scanf("%d",&T); while(T--) sol(); }
$T3$
$KD-tree$四維偏序就好了,據說用$Min-Max$反演可以變為三維偏序(咕咕咕)
上午模擬賽一直在罰坐...
模擬賽
打了一場模擬賽,又雙叒叕墊底了
$T1$想了半天差分和網路流,沒有分析出性質,還需要增強分析能力
$T2$設計狀態出了問題,一直卡在了重合部分,在容斥裡面沒想出來
$T3$博弈論$kill$
$T1$
//首先考慮左下角和右上角操作是沒用的 //所有的狀態可以由1解決,而且代價小 //考場上想到了差分+網路流,其實沒什麼關係. //考慮在矩陣上搞一搞事情,轉化一下矩陣 //a[i][j]=(c[i][j]+c[i+1][j]+c[i][j+1]+c[i+1][j+1])%2;這不就是(奇怪的)差分嗎... //對於操作1,我們是翻轉一個點,對於操作是,翻轉(x,y) //對於操作4,我們是翻轉四個點,全是1我們才會翻轉,而且只操作一次就好 //然後就很水了,這種結論題還是不怎麼好整 #include<bits/stdc++.h> #define INF 2147483647 #define int long long #define MAXN 1000005 using namespace std; int head[MAXN],nxt[MAXN],val[MAXN],to[MAXN],tot=1; int Num[505][505]; char mp[505][505]; int dis[MAXN]; int n,m,opt,Ans; queue<int>q; void add(int u,int v,int w) { // cout<<"add: "<<u<<" "<<v<<endl; tot++; to[tot]=v; val[tot]=w; nxt[tot]=head[u]; head[u]=tot; } bool bfs(int s,int t) { // for(int i=1;i<=t;i++) dis[i]=-1; memset(dis,-1,sizeof(dis)); dis[s]=0; q.push(s); while(!q.empty()) { int now=q.front(); q.pop(); for(int i=head[now];i!=-1;i=nxt[i]) { int y=to[i]; // cout<<"now: "<<now<<" "<<y<<endl; // system("pause"); if(dis[y]!=-1||!val[i]) continue; dis[y]=dis[now]+1; q.push(y); } } return dis[t]!=-1; } int dfs(int now,int ed,int flow) { if(now==ed) return flow; int rest=flow; for(int i=head[now];i!=-1;i=nxt[i]) { int y=to[i]; if(dis[y]!=dis[now]+1||!val[i]) continue; int k=dfs(y,ed,min(rest,val[i])); val[i]-=k; val[i^1]+=k; rest-=k; if(!k) dis[y]=-1; } return flow-rest; } signed main() { freopen("dream.in","r",stdin); freopen("dream.out","w",stdout); scanf("%d%d%d",&n,&m,&opt); if(opt==0) { for(int i=1;i<=n;i++) { scanf("%s",mp[i]+1); } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { Num[i][j]=(mp[i][j]=='W'?0:1); } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { Num[i][j]+=Num[i+1][j]+Num[i][j+1]+Num[i+1][j+1]; Num[i][j]%=2; } } bool flag=false; for(int i=1;i<n;i++) { for(int j=1;j<m;j++) { if(Num[i][j]&&Num[n][j]&&Num[i][m]&&Num[n][m]) { flag=true; goto EB; } } } EB:; int res=0; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { res+=Num[i][j]; } } if(!flag) { cout<<res; } else { cout<<(res-4)+3; } } else { memset(head,-1,sizeof(head)); int S=n+m+1,T=n+m+2; for(int i=1;i<=n;i++) { scanf("%s",mp[i]+1); } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { Num[i][j]=(mp[i][j]=='W'?0:1); } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { Num[i][j]+=Num[i+1][j]+Num[i][j+1]+Num[i+1][j+1]; Num[i][j]%=2; } } for(int i=1;i<n;i++) { for(int j=1;j<m;j++) { if(Num[i][j]&&Num[n][j]&&Num[i][m]) { add(i,j+n,1); add(j+n,i,0); // cout<<"add1: "<<i<<" "<<j+n<<endl; } } } for(int i=1;i<n;i++) add(S,i,1),add(i,S,0); for(int i=1;i<m;i++) add(i+n,T,1),add(T,i+n,0); int flow=0; while(bfs(S,T)) { flow+=dfs(S,T,INF); } Num[n][m]^=(flow&1); int res=0; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { res+=Num[i][j]; } } cout<<res-flow; } }
$T2$
//考試時候想到了容斥,需要處理一堆東西 //首先有效區間不多,可以離散化 //其次關於最大值不同的區間,可以分別計數 //而且小的優先順序更高,可以完全覆蓋最大值大的區間 //問題轉化也比較好說,首先分開考慮是必然的 //其次是,處理每一類最大值 //我們現在問題轉化為一個區間必須有一個點為最大值 //大部分都很好說,就是那個區間重疊不會處理了 //先說沒有重合的統計答案 //就是總方案-不合法的 //重合的話使用dp轉移 //大概明白了,其實就是把有斷點排序,找上一個區間染色 //然後列舉上一步決策,讓這一步合法就好了 #include<bits/stdc++.h> #define INF 2147483647 #define int long long #define mod 998244353 #define MAXN 2005 using namespace std; int T,n,Q,A; struct Node { int l,r,L,R,m; bool operator <(const Node &b)const { if(l!=b.l) return l<b.l; else r<b.r; } }q[MAXN],seg[MAXN<<2]; int num,b[MAXN<<1],cnt,Min[MAXN<<1],f[MAXN<<1][MAXN<<1];; bool cmp(Node x,Node y) { return x.m<y.m; } int my_pow(int a,int b) { int res=1; while(b) { if(b&1) { res=res*a%mod; } a=a*a%mod; b>>=1; } return res; } int calc(int x) { Node pos[MAXN<<1]; int tot=0,m=0,pl[MAXN<<1],pr[MAXN<<1]; for(int i=1;i<=num;i++) { if(Min[i]==x) { pos[++tot]=seg[i]; pl[++m]=seg[i].l; pr[m]=seg[i].r; } } if(tot==0) return -1; sort(pl+1,pl+m+1); sort(pr+1,pr+m+1); int lim[MAXN]; for(int i=1;i<=tot;i++) { lim[i]=0; } for(int i=1;i<=Q;i++) { if(q[i].m==x) { int L=lower_bound(pl+1,pl+m+1,q[i].L)-pl,R=upper_bound(pr+1,pr+m+1,q[i].R)-pr-1; lim[R]=max(lim[R],L); } } f[0][0]=1; for(int i=1;i<=tot;i++) { f[i][i]=0; int base0=my_pow(x-1,pos[i].r-pos[i].l+1); int base1=my_pow(x,pos[i].r-pos[i].l+1); for(int j=0;j<i;j++) { if(j>=lim[i]) { f[i][j]=f[i-1][j]*base0%mod; } else f[i][j]=0; f[i][i]=(f[i][i]+f[i-1][j]*(base1-base0+mod)%mod)%mod; } } int res=0; for(int i=0;i<=tot;i++) { res=(res+f[tot][i])%mod; } return res; } unordered_map<int,int>book; void solve() { scanf("%d%d%d",&n,&Q,&A); cnt=0; for(int i=1;i<=Q;i++) { scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].m); q[i].L=q[i].l,q[i].R=q[i].r; b[++cnt]=q[i].l,b[++cnt]=q[i].r; } sort(b+1,b+cnt+1); cnt=unique(b+1,b+cnt+1)-b-1; num=0; book.clear(); for(int i=1;i<=cnt;i++) { if(b[i-1]+1<=b[i]-1) seg[++num]={b[i-1]+1,b[i]-1,b[i-1]+1,b[i]-1,0}; seg[++num]={b[i],b[i],b[i],b[i],0},book[b[i]]=num; } if(seg[num].r!=n) seg[num+1]={seg[num].r+1,n,seg[num].r+1,n,0},num++; for(int i=1;i<=Q;i++) { q[i].l=book[q[i].l],q[i].r=book[q[i].r]; } memset(Min,63,sizeof(Min)); for(int i=1;i<=Q;i++) { for(int u=q[i].l;u<=q[i].r;u++) { Min[u]=min(Min[u],q[i].m); } } set<int>S; for(int i=1;i<=Q;i++) { S.insert(q[i].m); } int ans=1; for(int x:S) { int res=calc(x); if(res==-1) { printf("0\n"); return; } else ans=ans*res%mod; } for(int i=1;i<=num;i++) { if(Min[i]>A) ans=ans*my_pow(A,seg[i].r-seg[i].l+1)%mod; } printf("%lld\n",ans); return; } signed main() { freopen("value.in","r",stdin); freopen("value.out","w",stdout); scanf("%d",&T); while(T--) solve(); return 0; }
$T3$
//神奇的博弈問題 //開始有一些地方有石子,每次選擇滿足條件p,q和一個位置 //在這個位置減去一個石子,其餘位置加一個石子 //本題就是mod 2的情況下 //如果原來 #include <bits/stdc++.h> #define MAXN 300005 using namespace std; int n,q,SG[MAXN],cnt[350]; void sol(int x,int p,int w) { int c=0,y=x; for(int i=1;i<=q;i++) { if(y%p!=0) { break; } y/=p; c^=SG[y]; if(c<303)cnt[c]=x; } } void GSG(int x) { for(int i=1,j=2;;i++) { if(x%j!=0) break; sol(x,j,i); j*=2; } for(int i=1,j=3;;i++) { if(x%j!=0) break; sol(x,j,i); j*=3; } for(int i=0;;i++) { if(cnt[i]!=x) { SG[x]=i; return; } } } int main() { freopen("forward.in","r",stdin); freopen("forward.out","w",stdout); int T; scanf("%d",&T); for(int i=1,b=0,c=0;i<=T;i++) { c=0; memset(cnt,0,sizeof(cnt)); scanf("%d%d",&n,&q); for(int j=1;j<=n;j++) { GSG(j); } for(int j=1;j<=n;j++) { scanf("%d",&b); if(b==0) c^=SG[j]; } if(c==0) printf("lose\n"); else printf("win\n"); } return 0; }