在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下
两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个
结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
先)你能帮帮他吗?
题解
这题如果直接做的话,就是一个裸的树链剖分。
但也有一个更加巧妙的做法,如果把操作序列倒过来看,打标记就变成了删标记。
如果用一个数组来维护答案的话,这个操作相当于把指向这个点的位置的答案改成这个点的答案,可以直接用并查集来维护这一过程。
#include<iostream>
#include<cstdio>
#define N 100009
using namespace std;
int f[N],head[N],tot,n,q,a[N],fa[N],ans[N],top,tag[N];
char c[N];
struct dwd {
int n,to;
} e[N<<];
inline void add(int u,int v) {
e[++tot].n=head[u];
e[tot].to=v;
head[u]=tot;
}
int find(int x) {
return f[x]=f[x]==x?x:find(f[x]);
}
inline char getch() {
char c=getchar();
while(!isalpha(c))c=getchar();
return c;
}
int rd() {
int x=;
char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c)) {
x=(x<<)+(x<<)+(c^);
c=getchar();
}
return x;
}
void dfs(int u,int faa) {
for(int i=head[u]; i; i=e[i].n) {
int v=e[i].to;
if(v==faa)continue;
fa[v]=u;
if(!tag[v])f[v]=u;
else f[v]=v;
dfs(v,u);
}
}
int main() {
n=rd();
q=rd();
int u,v;
for(int i=; i<n; ++i)u=rd(),v=rd(),add(u,v),add(v,u);
tag[]=;
f[]=;
for(int i=; i<=q; ++i) {
c[i]=getch();
a[i]=rd();
if(c[i]=='C')tag[a[i]]++;
}
dfs(,);
for(int i=q; i>=; --i) {
if(c[i]=='C') {
tag[a[i]]--;
if(!tag[a[i]])f[a[i]]=fa[a[i]];
} else ans[++top]=find(a[i]);
}
for(int i=top; i>=; --i)printf("%d\n",ans[i]);
return ;
}

排序

在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题
,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排
序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q
位置上的数字。
题解
答案具有单调性,可以直接二分答案。
然后把大于这个数的标为1,小的标成0,然后那些操作直接一通做下来就好了。
只有01两种数,线段树直接统计就可以。
#include<iostream>
#include<cstdio>
#define N 30002
using namespace std;
int la[N<<],tr[N<<],a[N],ji,tag,n,m,l[N],r[N],ta[N],ans,q;
bool b[N];
inline void pushdown(int cnt,int l1,int l2){
int sum=tr[cnt];
if(la[cnt]==){
if(l1>=sum){
tr[cnt<<]=sum;sum=;
}
else{
tr[cnt<<]=l1;sum-=l1;
}
tr[cnt<<|]=sum;
}
else{
if(l2>=sum){
tr[cnt<<|]=sum;sum=;
}
else{
tr[cnt<<|]=l2;sum-=l2;
}
tr[cnt<<]=sum;
}
la[cnt<<]=la[cnt<<|]=la[cnt];la[cnt]=;
}
int dfs(int cnt,int l,int r){
if(l==r)return tr[cnt];
int mid=(l+r)>>;
if(la[cnt])pushdown(cnt,mid-l+,r-mid);
if(mid>=q)dfs(cnt<<,l,mid);
else dfs(cnt<<|,mid+,r);
}
void build(int cnt,int l,int r){
if(l==r){
tr[cnt]=b[l];
return;
}
int mid=(l+r)>>;
build(cnt<<,l,mid);build(cnt<<|,mid+,r);
tr[cnt]=tr[cnt<<]+tr[cnt<<|];//care
}
void gett(int cnt,int l,int r,int L,int R){
if(l>=L&&r<=R){
ji+=tr[cnt];
return;
}
int mid=(l+r)>>;
if(la[cnt])pushdown(cnt,mid-l+,r-mid);
if(mid>=L)gett(cnt<<,l,mid,L,R);
if(mid<R)gett(cnt<<|,mid+,r,L,R);
}
void returnn(int cnt,int l,int r,int L,int R){
if(l>=L&&r<=R){
la[cnt]=tag;
if(ji>=r-l+){
tr[cnt]=r-l+;
ji-=tr[cnt];
}
else{
tr[cnt]=ji;ji=;
}
return;
}
int mid=(l+r)>>;
if(tag==){
if(mid>=L)returnn(cnt<<,l,mid,L,R);
if(mid<R)returnn(cnt<<|,mid+,r,L,R);
}
else{
if(mid<R)returnn(cnt<<|,mid+,r,L,R);
if(mid>=L)returnn(cnt<<,l,mid,L,R);
}
tr[cnt]=tr[cnt<<]+tr[cnt<<|];
}
bool ch(int pos){
for(int i=;i<=n;++i)b[i]=a[i]>=pos?:;
build(,,n);
for(int i=;i<=m;++i){
ji=;
tag=ta[i];
gett(,,n,l[i],r[i]);
returnn(,,n,l[i],r[i]);
}
if(dfs(,,n))return ;
else return ;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)
scanf("%d",&a[i]);
for(int i=;i<=m;++i){scanf("%d%d%d",&ta[i],&l[i],&r[i]);if(!ta[i])ta[i]=;}
scanf("%d",&q);
int l=,r=n;
while(l<=r){
int mid=(l+r)>>;
if(ch(mid)){
ans=mid;
l=mid+;
}
else r=mid-;
}
cout<<ans;
return ;
}

序列

佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值

可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你
,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可
。注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:
1 2 3
2 2 3
1 3 3
1 1 31 2 4
选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:3 3 33 2 3选择子序列
为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求
游戏
在2016年,佳缘姐姐喜欢上了一款游戏,叫做泡泡堂。简单的说,这个游戏就是在一张地图上放上若干个炸弹,看
是否能炸到对手,或者躲开对手的炸弹。在玩游戏的过程中,小H想到了这样一个问题:当给定一张地图,在这张
地图上最多能放上多少个炸弹能使得任意两个炸弹之间不会互相炸到。炸弹能炸到的范围是该炸弹所在的一行和一
列,炸弹的威力可以穿透软石头,但是不能穿透硬石头。给定一张n*m的网格地图:其中*代表空地,炸弹的威力可
以穿透,可以在空地上放置一枚炸弹。x代表软石头,炸弹的威力可以穿透,不能在此放置炸弹。#代表硬石头,炸
弹的威力是不能穿透的,不能在此放置炸弹。例如:给出1*4的网格地图*xx*,这个地图上最多只能放置一个炸弹
。给出另一个1*4的网格地图*x#*,这个地图最多能放置两个炸弹。现在小H任意给出一张n*m的网格地图,问你最
多能放置多少炸弹
题解
考虑没有墙的情况,把每行每列看做点,如果哪个位置有炸弹,就在行和列之间连边,跑二分图匹配。
如果有墙,就在墙的右边和下面为这一行和这一列新开点就可以了。
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,a[][],tot2,tot1,tot,ans,pim[],vis[],head[];
char s[];
struct dv
{
int a,b;
}b[][];
struct fd
{
int next,to;
}an[];
void add(int u,int v)
{
an[++tot].next=head[u];
an[tot].to=v;
head[u]=tot;
}
bool dfs(int pos,int tim)
{
for(int i=head[pos];i;i=an[i].next)
if(vis[an[i].to]!=tim)
{
int v=an[i].to;
vis[v]=tim;
if(!pim[v]||dfs(pim[v],tim))
{
pim[v]=pos;
return true;
}
}
return false;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)
{
scanf("%s",s+);
for(int j=;j<=m;++j)
{
if(s[j]=='*')a[i][j]=;
else if(s[j]=='x')a[i][j]=;
else a[i][j]=;
if(a[i][j]!=)
{
if(a[i][j-]==||j==)b[i][j].a=++tot1;
else b[i][j].a=b[i][j-].a;
if(a[i-][j]==||i==)b[i][j].b=++tot2;
else b[i][j].b=b[i-][j].b;
if(a[i][j]==)add(b[i][j].a,b[i][j].b);
}
}
}
for(int i=;i<=tot1;++i)
if(dfs(i,i))ans++;
cout<<ans<<endl;
return ;
}

求和

在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心。

现在他想计算这样一个函数的值:
S(i, j)表示第二类斯特林数,递推公式为:
S(i, j) = j ∗ S(i − 1, j) + S(i − 1, j − 1), 1 <= j <= i − 1。
边界条件为:S(i, i) = 1(0 <= i), S(i, 0) = 0(1 <= i)
你能帮帮他吗?
题解
感觉这是这年省选最难的题了。
不过也是比较套路
第二类斯特林数有一个通项
S(n,m)=(1/m!)*∑(-1)i*C(m,i)*(m-i)n
然后把前面那个阶乘放到后面去。
S(n,m)=∑((-1)i/i!)*((m-i)n/(m-i)!)
再把它放回原来的式子里。
∑2jj!∑((-1)k/k!)*(∑(j-k)i/(j-k)!)
然后我们可以把后面两个东西看做卷积,做一遍NTT就可以了(得先把等比数列拆开)。
#include<iostream>
#include<cstdio>
#define N 300002
using namespace std;
typedef long long ll;
const int mod=;
const int Gi=;
const int G=;
ll a[N],b[N],rev[N],n,jie[N],ni[N],ans,l,L;
inline ll rd(){
ll x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
ll power(ll x,ll y){
ll ans=;
while(y){
if(y&)ans=(ans*x)%mod;
y>>=;x=(x*x)%mod;
}
return ans;
}
inline int ny(ll x){return power(x,mod-);}
inline void NTT(ll *a,int tag){
for(int i=;i<l;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int i=;i<l;i<<=){
ll wn=power(tag==?G:Gi,(mod-)/(i<<));
for(int j=;j<l;j+=(i<<)){
ll w=;
for(int k=;k<i;++k,(w*=wn)%=mod){
ll x=a[j+k],y=w*a[j+k+i]%mod;
a[j+k]=(x+y)%mod;a[j+k+i]=(x-y+mod)%mod;
}
}
}
}
int main(){
n=rd();jie[]=;
for(int i=;i<=n;++i)jie[i]=jie[i-]*i%mod;ni[n]=power(jie[n],mod-);
for(int i=n-;i>=;--i)ni[i]=ni[i+]*(i+)%mod;
for(int i=;i<=n;++i)a[i]=(power(-,i)*ni[i]+mod)%mod;
b[]=;b[]=n+;for(int i=;i<=n;++i)b[i]=(power(i,n+)-)*ny((i-)*jie[i]%mod)%mod;
l=,L=;
while(l<(n<<))l<<=,L++;
for(int i=;i<l;++i)rev[i]=(rev[i>>]>>)|((i&)<<(L-));
NTT(a,);NTT(b,);
for(int i=;i<l;++i)a[i]=a[i]*b[i]%mod;
NTT(a,-);int nn=ny(l);
for(int i=;i<l;++i)a[i]=a[i]*nn%mod;
for(int i=;i<=n;++i)ans=(ans+power(,i)*jie[i]%mod*a[i]%mod)%mod;
cout<<ans;
return ;
}

字符串

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公
共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?
题解
根据国际惯例,先二分长度。
然后找出c在后缀数组中的位置,既然已经二分了答案,那么可以左右分别二分,找出后缀数组上的一段区间满足我们二分的答案。
然后这段区间如果包含a~b-mid+1,相当于我们已经找到了答案。
现在问题就是判断区间里是否有某数,用主席树解决。
还有就是这题卡常,要预处理ST表的log。
// luogu-judger-enable-o2
// luogu-judger-enable-o2
%:pragma GCC optimize()
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 100002
#define inf 2e9
#define Re register
using namespace std;
int n,m,tong[N],sa[N],T[N],rank[N],y[N],height[N],p[N][],tr[N*],R[N*],L[N*],tot,a,b,c,d,logg[],lg2[N];
char s[N];
inline int minn(int x,int y){return x<y?x:y;}
inline int maxx(int x,int y){return x<y?y:x;}
inline void write(int a){
if(a>=)write(a/);
putchar(''+a%);
}
inline void writeln(int a){
if(a<){
a=-a; putchar('-');
}
write(a); puts("");
}
inline int rd(){
int x=;char c=getchar();
while(!isdigit(c)){c=getchar();}
while(isdigit(c)){x=x*+(c^);c=getchar();}
return x;
}
inline void qsort(){
for(Re int i=;i<=m;++i)tong[i]=;
for(Re int i=;i<=n;++i)tong[rank[i]]++;
for(Re int i=;i<=m;++i)tong[i]+=tong[i-];
for(Re int i=n;i>=;--i)sa[tong[rank[y[i]]]--]=y[i];
}
void SA(){
m=;
for(Re int i=;i<=n;++i)rank[i]=s[i],y[i]=i;
qsort();
for(Re int w=,p;p<n;m=p,w<<=){
p=;
for(Re int i=n-w+;i<=n;++i)y[++p]=i;
for(Re int i=;i<=n;++i)if(sa[i]>w)y[++p]=sa[i]-w;
qsort();swap(y,rank);rank[sa[]]=p=;
for(Re int i=;i<=n;++i)rank[sa[i]]=((y[sa[i-]]==y[sa[i]])&&(y[sa[i-]+w]==y[sa[i]+w]))?p:++p;
}
height[]=;
for(Re int i=,j;i<=n;++i){
if(rank[i]==)continue;
j=maxx(,height[rank[i-]]-);
while(s[i+j]==s[sa[rank[i]-]+j])j++;
height[rank[i]]=j;
p[rank[i]][]=j;
}
for(Re int i=;logg[i]<=n;++i)
for(Re int j=;j+logg[i]-<=n;++j)p[j][i]=minn(p[j][i-],p[j+logg[i-]][i-]);
}
inline int RMQ(int l,int r){
if(l>r)return inf;
int lo=lg2[r-l+];
return minn(p[l][lo],p[r-logg[lo]+][lo]);
}
bool query(int now,int la,int l,int r,int LL,int RR){
if(l>=LL&&r<=RR)return (tr[now]-tr[la])!=;
Re int mid=(l+r)>>;bool ans=;
if(mid>=LL)ans|=query(L[now],L[la],l,mid,LL,RR);if(ans)return ;
if(mid<RR)ans|=query(R[now],R[la],mid+,r,LL,RR);
return ans;
}
void upd(int &cnt,int la,int l,int r,int x){
cnt=++tot;L[cnt]=L[la];R[cnt]=R[la];tr[cnt]=tr[la]+;
if(l==r)return;Re int mid=(l+r)>>;
if(mid>=x)upd(L[cnt],L[la],l,mid,x);
else upd(R[cnt],R[la],mid+,r,x);
}
void build(int &cnt,int l,int r){
cnt=++tot;if(l==r)return;
int mid=(l+r)>>;build(L[cnt],l,mid);build(R[cnt],mid+,r);
}
bool check(int x,int len){
Re int l=,r=x,zuo=x,you=x;
while(l<=r){
Re int mid=(l+r)>>;
if(RMQ(mid+,x)>=len){
zuo=mid;r=mid-;
}else l=mid+;
}
l=x,r=n;
while(l<=r){
Re int mid=(l+r)>>;
if(RMQ(x+,mid)>=len){
you=mid;l=mid+;
}else r=mid-;
}
if(query(T[you],T[zuo-],,n,a,b-len+))return ;
else return ;
}
inline void gett(){
char c=getchar();while(!isalpha(c))c=getchar();n=;
while(isalpha(c))s[++n]=c,c=getchar();
}
int main(){
n=rd();m=rd();gett();
for(Re int i=;i<=;++i)logg[i]=(<<i);
for(Re int i=;i<=n;++i)lg2[i]=lg2[i>>]+;
SA();
build(T[],,n);
for(Re int i=;i<=n;++i)upd(T[i],T[i-],,n,sa[i]);///care!!!!!!!!!!!!!!
while(m--){
a=rd();b=rd();c=rd();d=rd();
Re int l=c,r=d,ans=c-;r=minn(r,c+b-a);
while(l<=r){
Re int mid=(l+r)>>;
if(check(rank[c],mid-c+)){ans=mid;l=mid+;}
else r=mid-;
}
writeln(ans-c+);
}
return ;
}
这一年的题好水

HEOI2016解题报告的更多相关文章

  1. TJOI2016 && HEOI2016 解题报告

    好吧我来写一波题解骗访问量QAQ 题目可以在cogs提交 bzoj4551~4456 D1T1 tree 树剖可做,然而有更简单的做法,10min搞定 维护一个并查集,时光倒流,如果当前点没有标记就把 ...

  2. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

  3. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...

  4. BZOJ 1051 最受欢迎的牛 解题报告

    题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[S ...

  5. 习题:codevs 2822 爱在心中 解题报告

    这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...

  6. 习题:codevs 1035 火车停留解题报告

    本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...

  7. 习题: codevs 2492 上帝造题的七分钟2 解题报告

    这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...

  8. 习题:codevs 1519 过路费 解题报告

    今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...

  9. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

随机推荐

  1. JS XMLHttpRequesst对象 http post的五种请求状态

    记录一下js中对http请求的几种状态,下附代码 readyState 存有 XMLHttpRequest 的状态.从 0 到 4 发生变化. 0: 请求未初始化 1: 服务器连接已建立 2: 请求已 ...

  2. MYSQL 表大小限制

    MySQL 3.22限制的表大小为4GB.由于在MySQL 3.23中使用了MyISAM存储引擎,最大表尺寸增加到了65536TB(2567 – 1字节).由于允许的表尺寸更大,MySQL数据库的最大 ...

  3. angularjs4+ionic3集成搭建

    1:安装一下cnpm用淘宝镜像安装npm install -g cnpm --registry=https://registry.npm.taobao.org 2:使用 cnpm命令安装(全局安装 A ...

  4. 游标cursor案例

  5. mysql [assword expired

    mysql 5.6 在使用Navicat在其他机器上进行远程登录数据库时 会出现 password expired ,需要重新设置一下密码. SET PASSWORD FOR 'root'@'%' = ...

  6. EXAMPLE FOR PEEWEE 多姿势使用 PEEWEE

    使用 PEEWEE 断断续续的差不多已经三个年头了,但是没有像这次使用这么多的特性和功能,所以这次一并记录一下,需要注意的地方和一些使用细节,之后使用起来可能会更方便. 因为是使用的 SQLAched ...

  7. DAY03、基本数据类型和运算符

    一.基本数据类型的使用 1.整型int: 作用:用来记录年龄.等级.数量 定义:age=18 使用:数学运算与比较运算: 例:print(10>3) print(10/3) 2.浮点型float ...

  8. 一、zipkin

    zipkin是Twitter基于google的分布式监控系统Dapper(论文)的开发源实现,zipkin用于跟踪分布式服务之间的应用数据链路(具体就是收集微服务之间的调用情况,然后处理调用之间数据延 ...

  9. vue自定义组件及传值

    1.使用 Vue.component() 方法注册组件 2.使用 props 属性传递参数 v-for="item in items": 遍历 Vue 实例中定义的名为 items ...

  10. 晨读笔记:CSS3选择器之属性选择器

    一.属性选择器 1.E[foo^="bar"]:该属性选择器描述的是选择属性以bar开头的元素,如: //所有以名称g_开头的div的字体颜色为红色div[name^=" ...