开场先看一遍题面,凭着错误的感觉t3叫naive是一个原因,312开局。然后就死的很惨。

T1

朴素暴力40pts,细想就有80pts,然而我只写了十分钟左右就爬回T3了,所以...

其实都是借口

正解:

转换一下题意,给定n个非负整数的可重集合,要求满足 \(\sum_{i=1}^{n}x_{i}=m\) ,那么一定有 \(diff\{x_{n}\}\le\sqrt{m}\) ,其中 \(diff\) 表示本质不同的数。

所以用并查集维护一下牌堆之间的关系,用树状数组统计个数即可。

Code
#include<cstdio>
#define MAX 100010
#define re register
#define int long long
namespace OMA
{
int n,m,num;
int w[MAX],p[MAX],cnt[MAX];
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
int w=1; s=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*=w,*this;
}
}cin;
struct BIT
{
int tree[MAX];
inline int lowbit(int x)
{ return x&-x; }
inline void update(int x,int y)
{
for(re int i=x; i<=n; i+=lowbit(i))
{ tree[i] += y; }
}
inline int query(int x)
{
int res = 0;
for(re int i=x; i; i-=lowbit(i))
{ res += tree[i]; }
return res;
}
}BIT;
inline void modify(int val)
{
for(re int i=p[val]; i<=num-1; i++)
{ w[i] = w[i+1],p[w[i]] = i; }
num--,p[val] = 0;
}
int fa[MAX],size[MAX];
inline int find(int x)
{ return x != fa[x]?fa[x] = find(fa[x]):fa[x]; }
inline void merge(int x,int y)
{
int r1 = find(x),r2 = find(y);
if(r1!=r2)
{
fa[r2] = r1;
BIT.update(size[r1],-1);
BIT.update(size[r2],-1);
if(--cnt[size[r1]]==0)
{ modify(size[r1]); }
if(--cnt[size[r2]]==0)
{ modify(size[r2]); }
size[r1] += size[r2];
BIT.update(size[r1],1);
if(++cnt[size[r1]]==1)
{ w[++num] = size[r1],p[size[r1]] = num; }
}
}
signed main()
{
cin >> n >> m;
for(int i=1; i<=n; i++)
{ fa[i] = i; size[i] = 1; }
BIT.update(1,n);
w[num = 1] = 1,cnt[1] = n;
for(re int i=1,opt; i<=m; i++)
{
cin >> opt;
if(opt==1)
{
int x,y;
cin >> x >> y;
merge(x,y);
}
else
{
int c,ans = 0; cin >> c;
int N = BIT.query(n);
//printf("N=%lld\n",N);
//printf("%lld %lld\n",num,w[num]);
for(re int j=1; j<=num; j++)
{
//printf("j=%lld\n",j);
if(w[j]+c-1>=n)
{ continue ; /*printf("QAQ %lld\n",c);*/ }
//printf("QAQ %lld ",w[j]);
ans += cnt[w[j]]*(N-BIT.query(w[j]+c-1));
if(!c)
{ ans -= cnt[w[j]]*cnt[w[j]]-(cnt[w[j]]-1)*cnt[w[j]]/2; }
}
printf("%lld\n",ans);
}
//printf("i=%lld\n",i);
}
return 0;
}
}
signed main()
{ return OMA::main(); }

T2

咕咕咕





怎么tm不说人话

upd on 08-13

今天T3是个点分治,没学过的我在权衡之下,滚回来改这题。

首先可以根据归并排序的过程和原序列,来得到归并排序的指针移动的概率,设为 \(p_{i,j}\) 表示当前归并排序过程中,两个指针分别指向 \(i,j\) 的概率。

求 \(p\) 数组就是在模拟归并排序的过程。

然后考虑dp,设 \(dp_{dep,i,j}\) 表示当前在归并排序的第 \(dep\) 层, 由 \(i\) 转移到 \(j\) 的概率,有了上边的 \(p\) 数组,就可以直接从 \(dep+1\) 转移到 \(dep\) ,转移时根据 \(a\) 的大小分类讨论。

最后答案直接相乘累加即可。

Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 510
#define re register
#define int long long
using std::sort;
namespace OMA
{
int n,a[N];
int p[N][N],dp[N][N][N];
const int mod = 998244353;
const int inv = 499122177;
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
int w=1; s=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*=w,*this;
}
}cin;
inline void merge_sort(int dep,int l,int r)
{
if(l==r)
{ dp[dep][l][r] = 1; return ; }
int mid = (l+r)>>1;
merge_sort(dep+1,l,mid),merge_sort(dep+1,mid+1,r);
memset(p,0,sizeof(p)); p[0][0] = 1;
for(re int i=0; i<=mid-l+1; i++)
{
for(re int j=0; j<=r-mid; j++)
{
if(i==mid-l+1&&j==r-mid)
{ continue ; }
if(i==mid-l+1)
{ (p[i][j+1] += p[i][j]) %= mod; }
else if(j==r-mid)
{ (p[i+1][j] += p[i][j]) %= mod; }
else if(a[i+l]<a[j+mid+1])
{ (p[i+1][j] += p[i][j]) %= mod; }
else if(a[i+l]>a[j+mid+1])
{ (p[i][j+1] += p[i][j]) %= mod; }
else if(a[i+l]==a[j+mid+1])
{
(p[i+1][j] += p[i][j]*inv%mod) %=mod;
(p[i][j+1] += p[i][j]*inv%mod) %= mod;
}
}
}
for(re int i=l; i<=r; i++)
{
for(re int j=0; j<=mid-l+1; j++)
{
for(re int k=0; k<=r-mid; k++)
{
if(j==mid-l+1&&k==r-mid)
{ continue ; }
if(j==mid-l+1)
{ (dp[dep][i][j+k+l] += dp[dep+1][i][k+mid+1]*p[j][k]%mod) %= mod; }
else if(k==r-mid)
{ (dp[dep][i][j+k+l] += dp[dep+1][i][j+l]*p[j][k]%mod) %= mod; }
else if(a[j+l]<a[k+mid+1])
{ (dp[dep][i][j+k+l] += dp[dep+1][i][j+l]*p[j][k]%mod) %= mod; }
else if(a[j+l]>a[k+mid+1])
{ (dp[dep][i][j+k+l] += dp[dep+1][i][k+mid+1]*p[j][k]%mod) %= mod; }
else if(a[j+l]==a[k+mid+1])
{
(dp[dep][i][j+k+l] += dp[dep+1][i][j+l]*p[j][k]%mod*inv%mod) %= mod;
(dp[dep][i][j+k+l] += dp[dep+1][i][k+mid+1]*p[j][k]%mod*inv%mod) %= mod;
}
}
}
}
sort(a+l,a+r+1);
}
signed main()
{
cin >> n;
for(re int i=1; i<=n; i++)
{ cin >> a[i]; }
merge_sort(1,1,n);
for(re int i=1,ans; i<=n; i++)
{
ans = 0;
for(re int j=1; j<=n; j++)
{ (ans += j*dp[1][i][j]%mod) %= mod; }
printf("%lld ",ans);
}
return 0;
}
}
signed main()
{ return OMA::main(); }

T3

一直在写,然而分并不高。

因为一直在用错误的思路,上厕所的时候还把自己搞掉了,然而回来后,继续乱搞。

乱搞:

测试点b分治

首先写个 \(O(n^2)\) 暴力,发现 \(n\le30000\) 随便跑,然后我们大胆一些其实是经由某b试出来的,当当前算出的\(r-l+1>700\) 时,停止枚举 \(r\) ,直接去更新答案。

没有正确性,但是能A。大雾。

正解:

st表+链表。

我们固定右端点,枚举左端点来考虑。

对于本题,有几个比较关键的性质:

  1. \(OR-AND\) 本质不同的位置有 \(2\log n\) 个,且本质相同的位置都是连续的
  2. \(OR-AND\) 递增
  3. \(MIN-MAX\) 递减

对于1,考虑 \(OR\) 的后缀和 \(suf_{i}\) ,对于任意一个满足 \(suf_{i}\neq sud_{i+1}\) 的位置,\(suf_{i+1}\) 相较于\(suf_{i}\) 必然是至少有一位由 0 变成了 1 。总的二进制位数为 \(log\) 级别,所以 \(OR\) 本质不同的位置共有 \(log n\) 个,\(AND\) 同理,所以 \(OR-AND\) 本质不同的位置共有 \(2log n\) 个。

实在不懂,可以手模一下,就明白了。

2,3很好理解,就不再说了。

当移动右端点时,考虑用链表来记录下左端点所有 \(OR-AND\) 不同的区段,就是用链表记录一下右端点,对于 \(OR-AND\) 相等的一段,\(MIN-MAX\) 是递减的,所以直接二分来搞,\(O(nlog^{2} n)\) 。

因为我们要找的是最长的,所以找到第一个合法的直接break掉即可,\(O(nlog n)\)

再用线段树维护一下答案,最后遍历叶子结点输出答案即可。

具体实现见code。

Code
#include<list>
#include<cstdio>
#define re register
#define MAX 1000100
using std::list;
int n,k,a[MAX];
struct node
{
int p,ro,dna;
inline int delta()
{ return ro-dna; }
};
list<node>LIST;
inline int max(int a,int b)
{ return a>b?a:b; }
inline int min(int a,int b)
{ return a<b?a:b; }
namespace ST
{
int log[MAX];
int f[MAX][20],g[MAX][20]; // xam,nim
inline void pre_work()
{
for(re int i=2; i<=n; i++)
{ log[i] = log[i>>1]+1; }
for(re int j=1; j<=log[n]; j++)
{
for(re int i=1; i<=n-(1<<j)+1; i++)
{
f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
g[i][j] = min(g[i][j-1],g[i+(1<<(j-1))][j-1]);
}
}
}
inline int query1(int l,int r)
{
int gol = log[r-l+1];
return max(f[l][gol],f[r-(1<<gol)+1][gol]);
}
inline int query2(int l,int r)
{
int gol = log[r-l+1];
return min(g[l][gol],g[r-(1<<gol)+1][gol]);
}
}using namespace ST;
namespace OMA
{
struct Segment_Tree
{
struct TREE
{ int l,r,len; }st[MAX<<2];
inline int ls(int p)
{ return p<<1; }
inline int rs(int p)
{ return p<<1|1; }
inline void build(int p,int l,int r)
{
st[p] = (TREE){l,r,-1};
if(l==r)
{ return ; }
int mid = (l+r)>>1;
build(ls(p),l,mid),build(rs(p),mid+1,r);
}
inline void update(int p,int l,int r)
{
if(l<=st[p].l&&st[p].r<=r)
{
st[p].len = max(st[p].len,r-l+1);
return ;
}
int mid = (st[p].l+st[p].r)>>1;
if(l<=mid)
{ update(ls(p),l,r); }
if(r>mid)
{ update(rs(p),l,r); }
}
inline void print(int p)
{
if(st[p].l==st[p].r)
{ printf("%d ",st[p].len); return ; }
st[ls(p)].len = max(st[ls(p)].len,st[p].len);
st[rs(p)].len = max(st[rs(p)].len,st[p].len);
print(ls(p)),print(rs(p));
}
}Tree;
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
int w=1; s=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*=w,*this;
}
}cin;
inline bool check(list<node>::iterator it,int l,int r)
{ /*printf("check %d %d\n",l,r);*/ return it->delta()+query2(l,r)-query1(l,r)>=k; }
signed main()
{
//freopen("data.in","r",stdin);
//freopen("my.out","w",stdout);
cin >> n >> k;
for(re int i=1; i<=n; i++)
{ f[i][0] = g[i][0] = (cin >> a[i],a[i]); }
pre_work(); Tree.build(1,1,n);
//printf("???\n");
for(re int i=1; i<=n; i++)
{
//printf("one\n");
for(auto it=LIST.begin(); it!=LIST.end(); it++)
{ it->ro |= a[i],it->dna &= a[i]; /*printf("??1\n");*/ }
//printf("two\n");
LIST.emplace_back((node){i,a[i],a[i]});
//printf("i=%d\n",i);
//printf("three\n");
for(auto it1=LIST.begin(),it2=next(it1); it2!=LIST.end(); it2++)
{
//printf("???2\n");
if(it1->delta()==it2->delta())
{ LIST.erase(it1); it1 = it2; }
else
{ ++it1; }
}
//printf("four\n");
for(auto it=LIST.begin(); it!=LIST.end(); it++)
{
//printf("QAQ\n");
if(check(it,it->p,i))
{
//printf("five\n");
int l = 1,r = it->p,res;
if(it!=LIST.begin())
{ l = prev(it)->p+1; }
while(l<=r)
{
int mid = (l+r)>>1;
if(check(it,mid,i))
{ r = mid-1,res = mid; }
else
{ l = mid+1; }
}
Tree.update(1,res,i);
break ;
}
}
//printf("end:%d\n",i);
}
Tree.print(1);
return 0;
}
}
signed main()
{ return OMA::main(); }

反思总结:

  1. 不要硬刚一道题,时间分配要有比重。
  2. 当前思路不可做,就换一种,哪怕是乱搞。
  3. 一些能优化的地方尽量去优化,不要直接冲傻瓜暴力。

不过话说这次读题没啥问题

noip36的更多相关文章

随机推荐

  1. 第13次抽考(IO流)

    1.将文本文件a.txt 复制成 b.txt.要求: a. 用逐个字符复制方式: b. 用逐行读写方式: c. 用字符数组方式 2.将压缩包a.rar复制成b.rar. 注意:复制前后手工打开文件,若 ...

  2. 『心善渊』Selenium3.0基础 — 23、Selenium元素等待

    目录 1.什么是元素等待 2.为什么要设置元素等待 3.Selenium中常用的等待方式 4.强制等待 5.隐式等待 (1)隐式等待介绍 (2)示例 6.显式等待 (1)显式等待介绍 (2)语法 (3 ...

  3. CTF反序列化逃逸

    刷了一下CTF反序列化的题,去年没有好好了解.又补了一次PHP,害太菜了.每天看看别人的博客真的可以鼓舞人.简单记录一下两道字符串逃逸问题 推荐一个反序列化总结的很好的笔记https://www.cn ...

  4. final修饰符(3)-基本类型变量和引用类型变量的区别

    final修饰基本类型变量 当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变 final修饰引用类型变量 当使用final修饰引用类型变量时,它保存的仅仅是一 ...

  5. linux下nginx访问ftp目录权限问题

    在将nginx目录设置为ftp目录访问时会报错:403 forbidden 原因在于nginx访问时账户问题,通过修改nginx.conf中的访问名解决 打开nginx.conf 修改user值,去掉 ...

  6. 一定要收藏的5个优秀的SpringCloud开源项目

    上一期为大家推荐了几个前端模板,没看过的点下面 一定要收藏的5个后台管理系统的前端框架 今天再为大家推荐几个优秀的SpringCloud开源脚手架项目,开箱即用,不管是学习还是开发新项目,都非常不错. ...

  7. 去掉返回的json中特殊字符

    private static String jsonString(String s) { char[] temp = s.toCharArray(); int n = temp.length; for ...

  8. odoo12里面的RPC【远程过程调用】

    odoo的RPC有两种:RPC API:1.xml-rpc                                                      2.json-rpc 案例   x ...

  9. SpringBoot添加Cors跨域配置,解决No 'Access-Control-Allow-Origin' header is present on the requested resource

    目录 什么是CORS SpringBoot 全局配置CORS 拦截器处理预检请求 什么是CORS 跨域(CORS)请求:同源策略/SOP(Same origin policy)是一种约定,由Netsc ...

  10. nuxt服务部署到云上全程记录

    首先,在使用脚手架nuxt-app中创建项目时,箭头选用不起作用,这是因为git bash在windows中交互问题,临时的解决办法是换用cmd 登录云服务器后,首先安装nodejs yum inst ...