3.26省选模拟+NOI-ONLINE
或许有了绝对不能退役的理由了
今日趣闻:
这三个人都是同机房的,卡最优解(大常数选手不参与)....以至于最优解第一页都是我们机房的(有图为证,共三人)
$NOI\ online$
$T1$
首先模拟一遍记录这个点当前单调栈前面位置,然后线段树(主席树)查询多少下标小于某个数的
#include#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#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 ) { vis[peo[p1][i]]=true; } cnt=0; for(int i=0;i ) { cnt+=(vis[peo[p2][i]]?1:0); } for(int i=0;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#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: "< 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: "< // 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 for(int j=1;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) { for(int j=1;j ) { if(Num[i][j]&&Num[n][j]&&Num[i][m]) { add(i,j+n,1); add(j+n,i,0); // cout<<"add1: "< } } } for(int i=1;i 1 ),add(i,S,0); for(int i=1;i1),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< flow; } }
$T2$
//考试时候想到了容斥,需要处理一堆东西 //首先有效区间不多,可以离散化 //其次关于最大值不同的区间,可以分别计数 //而且小的优先级更高,可以完全覆盖最大值大的区间 //问题转化也比较好说,首先分开考虑是必然的 //其次是,处理每一类最大值 //我们现在问题转化为一个区间必须有一个点为最大值 //大部分都很好说,就是那个区间重叠不会处理了 //先说没有重合的统计答案 //就是总方案-不合法的 //重合的话使用dp转移 //大概明白了,其实就是把有断点排序,找上一个区间染色 //然后枚举上一步决策,让这一步合法就好了 #include#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) { 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#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; }