[300iq Contest 1]简要题解
前言
老年选手的智商训练(1/∞)
题目链接
Angle Beats
考虑建图,发现每个∗*∗和+++的度数都为2,每个...的度数都为1
对于每个∗*∗和+++拆两个点,这两个点互相连边
对于一个∗*∗点,其中一个点向上/下的...连边,另一个向左右的...连边
对于一个+++点,两个点都向上下左右的...连边
考虑这张图的最大匹配,可以发现,答案为match-cnt,cnt为∗*∗和+++的数量
直接抄个带花树板子即可
Code
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;const int N=2e4+5,M=N<<5;int t[M],nxt[M],lst[N],l;
void add(int x,int y) {t[++l]=y;nxt[l]=lst[x];lst[x]=l;t[++l]=x;nxt[l]=lst[y];lst[y]=l;
}int n,m,tot,fa[N],match[N],vis[N],pre[N],mark[N],an[N],used[N],id;
char s[105][105];
queue<int> q;int Id(int x,int y) {return (x-1)*m+y;}int get(int x) {return fa[x]!=x?fa[x]=get(fa[x]):x;}int lca(int x,int y) {id++;x=get(x);y=get(y);while (vis[x]!=id) {vis[x]=id;x=get(pre[match[x]]);if (y) swap(x,y);}return x;
}void merge(int x,int y) {fa[get(x)]=get(y);}void flower(int x,int r) {for(;x!=r;) {int y=match[x],z=pre[y];if (get(z)!=r) pre[z]=y;if (mark[y]==2) q.push(y);mark[y]=1;if (mark[z]==2) q.push(z);mark[z]=1;merge(x,y);merge(y,z);x=z;}
}void find(int S) {fo(i,1,tot) fa[i]=i,pre[i]=mark[i]=0;while (!q.empty()) q.pop();q.push(S);mark[S]=1;while (!q.empty()) {int x=q.front();q.pop();rep(i,x) {int y=t[i];if (get(y)==get(x)) continue;if (mark[y]==2) continue;if (!mark[y]) {mark[y]=2;pre[y]=x;if (!match[y]) {for(int z=y,tmp;z;z=tmp) tmp=match[pre[z]],match[z]=pre[z],match[pre[z]]=z;return;}mark[match[y]]=1;q.push(match[y]);} else {int z=lca(x,y);if (get(x)!=z) pre[x]=y;if (get(y)!=z) pre[y]=x;flower(x,z);flower(y,z);}}}
}void fill(int p) {int x=(p-1)/m+1,y=(p-1)%m+1;if (x>1) used[an[Id(x-1,y)]]=id;if (x<n) used[an[Id(x+1,y)]]=id;if (y>1) used[an[Id(x,y-1)]]=id;if (y<m) used[an[Id(x,y+1)]]=id;
}int main() {scanf("%d%d",&n,&m);fo(i,1,n) scanf("%s",s[i]+1);tot=n*m;fo(i,1,n)fo(j,1,m) {int x=Id(i,j);if (s[i][j]=='*') {int y=++tot;add(x,y);if (i>1&&s[i-1][j]=='.') add(x,Id(i-1,j));if (i<n&&s[i+1][j]=='.') add(x,Id(i+1,j));if (j>1&&s[i][j-1]=='.') add(y,Id(i,j-1));if (j<m&&s[i][j+1]=='.') add(y,Id(i,j+1));}if (s[i][j]=='+') {int y=++tot;add(x,y);if (i>1&&s[i-1][j]=='.') add(x,Id(i-1,j)),add(y,Id(i-1,j));if (i<n&&s[i+1][j]=='.') add(x,Id(i+1,j)),add(y,Id(i+1,j));if (j>1&&s[i][j-1]=='.') add(x,Id(i,j-1)),add(y,Id(i,j-1));if (j<m&&s[i][j+1]=='.') add(x,Id(i,j+1)),add(y,Id(i,j+1));}}fo(i,1,tot) if (!match[i]) find(i);fo(i,1,tot) fa[i]=i;int y=n*m;id=0;fo(i,1,n)fo(j,1,m)if (s[i][j]=='*'||s[i][j]=='+') {int x=Id(i,j);y++;if (match[x]!=y&&match[x]&&match[y]) {id++;fill(x);fill(match[x]);fill(match[y]);int ret=1;while (used[ret]==id) ret++;an[x]=an[match[x]]=an[match[y]]=ret;} }fo(i,1,n) {fo(j,1,m) {int x=Id(i,j);putchar(an[x]?'a'+an[x]-1:s[i][j]);}puts("");}return 0;
}
Best Subsequence
这东西是一个环
显然最小值一定会取(证明反证一下
那么我们把最小值shift到开头,二分答案,后面的设个Dp[i]表示以i结尾最多能取多少个
用线段树维护即可
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;typedef long long ll;int read() {char ch;for(ch=getchar();ch<'0'||ch>'9';ch=getchar());int x=ch-'0';for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x;
}const int N=2e5+5;int n,k,w[N],a[N],b[N],id[N],o;
int tr[N<<2];void Ins(int v,int l,int r,int x,int y) {tr[v]=max(tr[v],y);if (l==r) return;int mid=l+r>>1;if (x<=mid) Ins(v<<1,l,mid,x,y);else Ins(v<<1|1,mid+1,r,x,y);
}int Que(int v,int l,int r,int x,int y) {if (x>y) return 0;if (x<=l&&r<=y) return tr[v];int mid=l+r>>1,tmp=0;if (x<=mid) tmp=max(tmp,Que(v<<1,l,mid,x,y));if (y>mid) tmp=max(tmp,Que(v<<1|1,mid+1,r,x,y));return tmp;
}int Id(int x) {if (x<b[1]) return 0;if (x>=b[o]) return o;return upper_bound(b+1,b+o+1,x)-b-1;
}int check(int x) {if (a[1]>=x) return 0;fo(i,1,o<<2) tr[i]=0;Ins(1,1,o,1,1);int ret=0;fo(i,2,n) {if (a[i]>=x) continue;int now=Que(1,1,o,1,Id(x-a[i]))+1;if (a[i]+a[1]<=x) ret=max(ret,now);Ins(1,1,o,id[i],now);}return ret;
}int main() {n=read();k=read();int p=0,mn=1e9+5;fo(i,1,n) {w[i]=read();if (w[i]<mn) mn=w[i],p=i;}fo(i,p,n) a[++a[0]]=w[i];fo(i,1,p-1) a[++a[0]]=w[i];fo(i,1,n) b[i]=a[i];sort(b+1,b+n+1);o=unique(b+1,b+n+1)-b-1;fo(i,1,n) id[i]=Id(a[i]);ll l=1,r=2e9,ans=0;while (l<=r) {ll mid=l+r>>1;if (check(mid)>=k) ans=mid,r=mid-1;else l=mid+1;}printf("%lld\n",ans);return 0;
}
Cool Pairs
考虑令a取遍-n~-1
对于每个b[q[i]],其对答案的贡献为[0,q[i])
那么我们不如取一个点x,使得x之前的b[q[i]]的贡献为q[i]-1,x把k补齐,其余贡献为0
构造方法显然
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;typedef long long ll;ll read() {char ch;for(ch=getchar();ch<'0'||ch>'9';ch=getchar());ll x=ch-'0';for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x;
}const int N=3e5+5;int n,a[N],b[N],c[N];
ll k;int main() {n=read();k=read();fo(i,1,n) a[read()]=-n+i-1;fo(i,1,n) {int x=read();if (!k) b[x]=n;else if (x-1<=k) k-=x-1;else {fo(j,1,x-1) c[j]=a[j];nth_element(c+1,c+k+1,c+x);b[x]=-c[k+1];k=0;}}puts("Yes");fo(i,1,n) printf("%d ",a[i]);puts("");fo(i,1,n) printf("%d ",b[i]);puts("");return 0;
}
Date
显然把所有区间按权值从大到小排序,能取就取
然后就是原题不解释
直接抄过来改一改就过了
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;typedef long long ll;int read() {char ch;for(ch=getchar();ch<'0'||ch>'9';ch=getchar());int x=ch-'0';for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x;
}const int N=3e5+5;
const ll inf=1e18;int n,m,c[N],cnt[N],id[N],rk[N];
ll a[N],mx[N<<2],mn[N<<2],tmx[N<<2],tmn[N<<2];void add(int x,ll z,int opt) {if (opt==0) mx[x]+=z,tmx[x]+=z;if (opt==1) mn[x]+=z,tmn[x]+=z;
}void down(int v) {if (tmx[v]) {add(v<<1,tmx[v],0);add(v<<1|1,tmx[v],0);tmx[v]=0;}if (tmn[v]) {add(v<<1,tmn[v],1);add(v<<1|1,tmn[v],1);tmn[v]=0;}
}void update(int v) {mx[v]=max(mx[v<<1],mx[v<<1|1]);mn[v]=min(mn[v<<1],mn[v<<1|1]);
}void modify(int v,int l,int r,int x,int y,ll z,int opt) {if (x>y) return;if (x<=l&&r<=y) {add(v,z,opt);return;}int mid=l+r>>1;down(v);if (x<=mid) modify(v<<1,l,mid,x,y,z,opt);if (y>mid) modify(v<<1|1,mid+1,r,x,y,z,opt);update(v);
}ll query_min(int v,int l,int r,int x,int y) {if (x<=l&&r<=y) return mn[v];int mid=l+r>>1;ll tmp=inf;down(v);if (x<=mid) tmp=min(tmp,query_min(v<<1,l,mid,x,y));if (y>mid) tmp=min(tmp,query_min(v<<1|1,mid+1,r,x,y));return tmp;
}ll query_max(int v,int l,int r,int x,int y) {if (x<=l&&r<=y) return mx[v];int mid=l+r>>1;ll tmp=-inf;down(v);if (x<=mid) tmp=max(tmp,query_max(v<<1,l,mid,x,y));if (y>mid) tmp=max(tmp,query_max(v<<1|1,mid+1,r,x,y));return tmp;
}struct Q{int l,r,k,id;}q[N];
bool cmp(Q a,Q b) {return a.k>b.k;}void init() {m=read();n=read();fo(i,1,n) a[i]=read();fo(i,1,m) q[i].l=read(),q[i].r=read(),q[i].k=read(),q[i].id=i;sort(q+1,q+m+1,cmp);fo(i,1,m) rk[i]=q[i].id;
}void prepare() {fo(i,1,m) {cnt[q[i].l]++;cnt[q[i].r+1]--;}fo(i,1,n) cnt[i]+=cnt[i-1];int Id=0;fo(i,1,n) if (cnt[i]) id[i]=++Id,c[Id]=a[i];fo(i,1,m) q[i].l=id[q[i].l],q[i].r=id[q[i].r];n=Id;fo(i,1,n) a[i]=c[i];fo(i,1,n) a[i]+=a[i-1];
}int main() {init();prepare();fo(i,1,m) {modify(1,1,m,rk[i],rk[i],-a[q[i].r],0);modify(1,1,m,rk[i],rk[i],-a[q[i].l-1],1);}ll ans=0;fo(i,1,m) {int t=rk[i];ll x=query_min(1,1,m,1,t)-query_max(1,1,m,t,m);x=min(x,1ll);ans+=x*q[i].k;modify(1,1,m,rk[i],m,x,0);modify(1,1,m,rk[i]+1,m,x,1);}printf("%lld\n",ans);return 0;
}
Expected Value
平面图的边数<=3n-6
走k步到的概率是是一个n阶递推,先求出前2n项,用BM求出递推式
然后答案可以写成P′(1)P'(1)P′(1),又P(x)=A(x)1−B(x)P(x)={A(x)\over 1-B(x)}P(x)=1−B(x)A(x)
直接暴力求导
抄个板子
Code
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
#define pb(a) push_back(a)
using namespace std;typedef long long ll;const int N=6e3+5,Mo=998244353;int pwr(int x,int y) {int z=1;for(;y;y>>=1,x=(ll)x*x%Mo)if (y&1) z=(ll)z*x%Mo;return z;
}vector<int> to[N];
void add(int x,int y) {to[x].pb(y);}int n,m,ty,deg[N],f[N],g[N],p[N],q[N],inv[N],x,y;
vector<int> h;void inc(int &x,int y) {x=x+y>=Mo?x+y-Mo:x+y;}namespace BM{vector<int> h[N];int cnt,fail[N],d[N],mx;vector<int> work(int n,int *a) {h[cnt=mx=0].clear();fo(i,1,n) {int now=-a[i];for(unsigned j=0;j<h[cnt].size();j++) (now+=(ll)a[i-j-1]*h[cnt][j]%Mo)%=Mo;d[i]=now;if (!now) continue;fail[cnt]=i;if (!cnt) {h[++cnt].clear();h[cnt].resize(i);continue;}vector<int> r;r.resize(i-fail[mx]-1);int mul=-(ll)now*pwr(d[fail[mx]],Mo-2)%Mo;r.pb(-mul);for(unsigned j=0;j<h[mx].size();j++) r.pb((ll)h[mx][j]*mul%Mo);if (r.size()<h[cnt].size()) r.resize(h[cnt].size());for(unsigned j=0;j<h[cnt].size();j++) (r[j]+=h[cnt][j])%=Mo;if (i-fail[mx]+h[mx].size()>=h[cnt].size()) mx=cnt;h[++cnt]=r;}return h[cnt];}
}int main() {scanf("%d",&n);fo(i,1,n) deg[i]=0,to[i].clear();fo(i,1,n) scanf("%d%d",&x,&y);scanf("%d",&m);fo(i,1,m) {scanf("%d%d",&x,&y);add(x,y);add(y,x);deg[x]++;deg[y]++;}fo(i,1,n) inv[i]=pwr(deg[i],Mo-2);fo(i,1,n) f[i]=0;f[1]=1;p[0]=0;fo(i,1,n*2) {memset(g+1,0,n*4);fo(j,1,n) {int now=(ll)f[j]*inv[j]%Mo;for(int k:to[j]) inc(g[k],now);}memcpy(f+1,g+1,n*4);p[i]=f[n];f[n]=0;}h=BM::work(n*2,p);int m=h.size();fo(i,1,n*2) q[i]=0;fo(i,0,m-1) fo(j,0,m-i-1) (q[i+j+1]+=(ll)p[i]*h[j]%Mo)%=Mo;fo(i,1,m) q[i]=(p[i]-q[i])%Mo;int a=0,da=0,b=1,db=0;fo(i,1,m) {(a+=q[i])%=Mo,(da+=(ll)q[i]*i%Mo)%=Mo;(b-=h[i-1])%=Mo,(db-=(ll)h[i-1]*i%Mo)%=Mo;}int ans=((ll)da*b%Mo-(ll)db*a%Mo)%Mo;ans=(ll)ans*pwr((ll)b*b%Mo,Mo-2)%Mo;printf("%d\n",(ans+Mo)%Mo);return 0;
}
Free Edges
操作相当于每次删去度数为1的点
显然条件为无环
直接数连通块数
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;int read() {char ch;for(ch=getchar();ch<'0'||ch>'9';ch=getchar());int x=ch-'0';for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x;
}const int N=1e5+5;int n,m,f[N],k;int get(int x) {return f[x]?f[x]=get(f[x]):x;}void merge(int x,int y) {x=get(x);y=get(y);if (x==y) return;f[y]=x;k++;
}int main() {n=read();m=read();fo(i,1,m) {int x=read(),y=read();merge(x,y);}printf("%d\n",m-k);return 0;
}
Graph Counting
题解告诉我们,一个图合法的充要条件为:
存在x个点和其余所有点连边,并且删去这x个点,剩余的图为x+2个奇团
必要性很好证,充分性不会QwQ
考虑枚举x,剩余的相当于把2n-x分成x+2个奇数
相当于把2n-2x-2分成x+2个偶数
相当于把n-x-1分成x+2个非负整数
相当于把n+1分成x+2个正整数
考虑对所有x重合,相当于将n+1分成>=2个正整数
显然是P(n+1)-1
用五边形数求分拆数即可
exp没跑过去太真实了
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;const int N=5e5+5,Mo=998244353;void inc(int &x,int y) {x=x+y>=Mo?x+y-Mo:x+y;}int n,f[N];int Five(int x) {return x*(3*x-1)/2;}int main() {scanf("%d",&n);n++;f[0]=f[1]=1;fo(i,2,n) {for(int j=1;Five(j)<=i;j++) {inc(f[i],(j&1)?f[i-Five(j)]:Mo-f[i-Five(j)]);if (Five(-j)<=i) inc(f[i],(j&1)?f[i-Five(-j)]:Mo-f[i-Five(-j)]);}}printf("%d\n",f[n]-1);return 0;
}
Hall’s Theorem
某套SD集训的题的A
考虑左边的每个点都向右边的每个点的一个前缀连边
设i向前ai个点连边,把ai从小到大排序
考虑好的集合的数量,容易发现为∑i=1n∑j=0ai−1(i−1j)\sum_{i=1}^{n}\sum_{j=0}^{ai-1}\binom{i-1}{j}∑i=1n∑j=0ai−1(ji−1)
从大到小构造即可
n这么小是因为SPJ只能做2^n…
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;typedef long long ll;const int N=35;int e[N][N],n,k,C[N][N],a[N*N][2],tot;int main() {scanf("%d%d",&n,&k);fo(i,0,n) {C[i][0]=1;fo(j,1,i) C[i][j]=C[i-1][j]+C[i-1][j-1];}fo(i,0,n) fo(j,1,n) C[i][j]+=C[i][j-1];k=(1<<n)-k-1;int j=n;fd(i,n,1) {while (j>=1&&C[i-1][j-1]>k) j--;fo(w,1,j) {++tot;a[tot][0]=i;a[tot][1]=w;}k-=C[i-1][j-1];}printf("%d\n",tot);fo(i,1,tot) printf("%d %d\n",a[i][0],a[i][1]);return 0;
}
Interesting Graph
选出的集合必须保证至少存在两个点不连通否则GG
=>连通块大小<=6
对每个连通块暴力,然后分治FFT+多点求值
6个点的本质不同的图很少
可以对每种图求出数量最后直接求∏(P(x))^k
Code
咕咕咕
Jealous Split
题解告诉我们:选出平方和最小的划分方法,这样一定合法
考虑证明,如果相邻两段和为a,b,x是a最右边的值或者b最左边的值
如果a-b<=x则显然合法
若a-b>x我们可以变成a-x,b+x,那么平方和会变小
_ (:з」∠) _
凸优化+斜率优化即可
Code
咕咕咕
Knowledge
题解告诉我们这个是"The presentation of group of Tetrahedral symmetry"…
看不懂没关系,考虑另一个东西
手玩发现对于每个串可以通过若干次操作得到一个唯一的最小的串
这个最小的串不会很多,直接矩乘
Code
咕咕咕
[300iq Contest 1]简要题解相关推荐
- 300iq Contest 3简要题解
和dcx打了一场300iq contest 3,垫底了. A: 有两个做法. 第一个做法是考虑对于一个合法的点集SSS,一定存在一个树上的连通块,使得从连通块中任何一点,距离SSS中任一点不超过x2\ ...
- Atcoder Yahoo Programming Contest 2019 简要题解
A-C 直接放代码吧. A int n,k; int main() {n=read();k=read();puts(k<=(n+1)/2?"YES":"NO&quo ...
- Noip 2014酱油记+简要题解
好吧,day2T1把d默认为1也是醉了,现在只能期待数据弱然后怒卡一等线吧QAQ Day0 第一次下午出发啊真是不错,才2小时左右就到了233,在车上把sao和fate补掉就到了= = 然后到宾馆之后 ...
- c语言1106回文数,Codeforces 1106 简要题解
A题 传送门 读错题还能过样例我给自己点个赞. 题意简述:给一个010101网格SSS,问满足Si,j=Si+1,j+1=Si+1,j−1=Si−1,j−1=Si−1,j+1S_{i,j}=S_{i+ ...
- 湖南省第十届蓝狐网络杯大学生计算机程序设计竞赛,2019年湖南省大学生计算机程序设计竞赛 (HNCPC2019) 简要题解...
2019年湖南省大学生计算机程序设计竞赛 (HNCPC2019) 简要题解 update10.01 突然发现叉姐把这场的题传到牛客上了,现在大家可以有地方提交了呢. 不知道该干什么所以就来水一篇题解 ...
- 杂题记录及简要题解(一)
一些前几天做过的还不错的但是不是太想专门花一整篇博客的篇幅去写的题就简要地记录在这里. 说是简要题解,其实写得还是挺详细的.之后的杂题记录可能就会写得简略一点. CF1060E Sergey and ...
- BJOI2018简要题解
BJOI2018简要题解 D1T1 二进制 题意 pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是 \(3\) 的倍数.他想研究对于二进制,是否也有类似的性质. 于是他生 ...
- 「题解」300iq Contest 2 B Bitwise Xor
本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:gym102331B. 题意概述 给你一个长度为 nnn 的序列 aia_iai,求一个最长的子序列满足所有子序列中的元 ...
- 「题解」300iq Contest 2 H. Honorable Mention
本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:gym102331H. 题意概述 给定一个长度为 n n n 的序列 a a a,有 q q q 次询问,每次询问给定三个 ...
最新文章
- 【Linux入门到精通系列讲解】内存管理malloc和free函数
- 查看电脑python虚拟环境-Windows系统下,Python虚拟环境搭建
- python--常用模块:collections 、time、random
- 走过2011---年终总结
- Spring MVC:使用基于Java的配置创建一个简单的Controller
- win8安装python3后不能编程_Win8.1 64位系统,python3.4 肿么安装gevent成功后不能用
- php查看mysql连接数_查看mysql当前连接数
- AcWing 837. 连通块中点的数量
- 人人都应该学习并成为优秀的产品经理,因为你要为《你自己》这个产品负责到底
- java dateutils详解_java DateUtils
- Tableau最新版安装下载
- cdr X6 64位32位缩略图补丁包
- 土豆系统 Ghost xp3 装机版
- UI设计师必备技能——点击进入的网页设计全攻略
- 利用gitlab pages和hexo搭建一个个人博客
- c#录音和放音,超简单!不用DirectX
- dhcp failover linux,Centos7 安装 DHCP 4.1 服务器配置及热备
- MATLAB 正态(高斯)分布概率密度函数
- DEV05 GBase 8a MPP Cluster 数据库性能优化
- 计算机主板知识,电脑硬件及电脑配置知识大全-主板知识