[考试总结]noip模拟5
T1:
看到这个题目,又短,又简单,一看就是 暴力,然后开始码,暴力很好打,会语言就行,唯一一点遗憾就是没有在 2min
之内打出来这个题目的暴力
但是在打完 laji nb 的T1暴力之后,心中首先是一阵小窃喜,之后突然想到 这个题目不会有人打不出来的,所以40pts一点作用没有,之后开始想 伪正解
\(n,m<=10^5\)
如果使用 \(sort\) 的话,那么一定就是严格的 \(\mathcal O(nlog_2n)\),那么一定只会拿到 \(40pts\),但是,如果使用桶排序,那么最优的复杂度就可以达到 \(\mathcal O(26nk)\)
但是,事实证明,我错了
\(k\)最多可以退化成 \(n\),然而这些数据对于桶排,全部会变成\(n\)
我枯了
但是,桶排并不是一无是处,正解便就是使用线段树优化桶排,对于每一个线段树上的节点,我们维护:
s[26]
l
r
debt
然后复杂度就是 \(\mathcal O(26^2 n)\)
\(\huge{\text{完全可过}}\)
\(code\):
#include<cmath>
#include<queue>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
//#define int long long
#define m(c,num) memset(c,num,sizeof c)
#define inf 0x7f7f7f7f
#define debug cout<<"debug"<<endl
#define freopen eat = freopen
#define scanf a14 = scanf
#define eps (1e-8)
namespace xin_io
{
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
char buf[1<<20],*p1 = buf,*p2 = buf; FILE *eat; int a14;
inline void openfile() {freopen("t.txt","r",stdin);}
inline void outfile() {freopen("com.txt","w",stdout);}
inline int get()
{
int s = 0,f = 1;register char ch = gc();
while(!isdigit(ch))
{if(ch == '-') f = -1;ch = gc();}
while(isdigit(ch))
{s = s * 10 + ch - '0'; ch = gc();}
return s * f;
}
}
const int maxn = 1e6+10;
using namespace xin_io;
namespace xin
{
class xin_tree{public:int l,r;int debt,s[27];}t[maxn]; //there I use a class to store my segment_tree
int n;
char s[maxn];
#define ls(fa) (fa << 1)
#define rs(fa) (fa << 1 | 1)
inline void up(int fa)
{for(register int i=0;i<26;++i) t[fa].s[i] = t[ls(fa)].s[i] + t[rs(fa)].s[i];} //this is pushup
void build(int fa,int l,int r) //first build the segment
{
t[fa].l = l; t[fa].r = r; t[fa].debt = -1;
if(l == r) {t[fa].s[s[l] - 'a']++;return ;}
register int mid = l + r >> 1;
build(ls(fa),l,mid); build(rs(fa),mid+1,r);
up(fa);
}
int sum[27];
inline void down(int fa) //lazytag is here
{
if(t[fa].debt == -1) return;
for(register int i=0;i<26;++i) t[ls(fa)].s[i] = t[rs(fa)].s[i] = 0;
t[ls(fa)].debt = t[rs(fa)].debt = t[fa].debt;
t[ls(fa)].s[t[fa].debt] = t[ls(fa)].r - t[ls(fa)].l + 1;
t[rs(fa)].s[t[fa].debt] = t[rs(fa)].r - t[rs(fa)].l + 1;
t[fa].debt = -1;
}
void modify(int fa,int l,int r,int val) //to modify the data in the segment_tree
{
if(r < t[fa].l or l > t[fa].r )return; //use another method to stop the illegal branch
if(t[fa].l >= l and t[fa].r <= r)
{
for(register int i=0;i<=26;++i) t[fa].s[i] = 0;
t[fa].s[val] = t[fa].r - t[fa].l + 1;
t[fa].debt = val;return;
}
down(fa);
modify(ls(fa),l,r,val); modify(rs(fa),l,r,val);
up(fa);
}
void query(int fa,int l,int r,int *ans)
{
if(r < t[fa].l or l > t[fa].r) return ;
if(t[fa].l >= l and t[fa].r <= r)
{
for(register int i=0;i<=25;++i)
ans[i] += t[fa].s[i];
return ;
}
down(fa);
query(ls(fa),l,r,ans); query(rs(fa),l,r,ans);
}
inline void do_sort(int l,int r,int op)
{
memset(sum,0,sizeof(sum));
query(1,l,r,sum);
if(op)
{
for(register int i=0;i<=25;++i)
{
if(!sum[i]) continue;
modify(1,l,l+sum[i]-1,i);
l += sum[i];
}
}
else
{
for(register int i=25;i>=0;--i)
{
if(!sum[i]) continue;
modify(1,l,l+sum[i]-1,i);
l += sum[i];
}
}
}
void output(int fa)
{
if(t[fa].l == t[fa].r)
{
for(register int i=0;i<=25;++i)
if(t[fa].s[i])
putchar(i + 'a');
return ;
}
down(fa);
output(ls(fa)); output(rs(fa));
}
inline short main()
{
#ifndef ONLINE_JUDGE
openfile();
#endif
int m;
scanf("%d%d%s",&n,&m,s+1);
build(1,1,n);
for(register int i=1;i<=m;++i)
{
register int l,r,op;
scanf("%d%d%d",&l,&r,&op);
do_sort(l,r,op);
}
output(1);
return 0;
}
}
signed main() {return xin::main();}
T2:
一眼暴力,可以 \(20pts\),但是我以为会全部暴掉。。。。
\(\huge{\text{我大意了}}\)
\(\huge{\text{没有闪}}\)
所以正解是 \(\mathcal O(n)\) \(dp\),
\[f_{i+1,j+1}+=f_{i,j}* (r_{i+1} - j)
\]
\[f_{i,j} = f_{i,j} * A(l_i-l_{i-1},i-j-l_{i-1})
\]
这方程我暂时是想不出来
T3:
首先 (⌊2x2n⌋+2x)mod2n是什么呢?我们先看,⌊2x2n⌋=⌊x2n−1⌋,当 x≥2n−1的二进制取后 n 位,第 1 位一定为 1.(比如 n=2,则 3 的二进制是 11,2 的二进制是 10)。否则第 1 位为 0. 然后 2x显然是把 x 左移 1 位,再模一下 2n
就是取后面 n 位。所以整个流程出来就是把 x 二进制下的第一位移到最后一位。
这说明了什么?原来 x 的取值范围是 [0,2n)
,在被你的对手搞事之后的数取值范围还是 [0,2n)
。
现在你的对手很心急,他决定在你异或 i 个数后再对你的 x 搞事,可是你还没开始异或他就要搞事了。怎么办?前 i 个数也只能做同样的操作(即将二进制下第一位移到最后一位),这样你在异或前 i 个数的时候二进制数位和搞事后的 x 是一一对应的。
我们令 a[i] 为异或的第 i 个数,b[i] 为 a[i] 被搞事的结果。那么我们求出 suf[i] 为 a 的后缀异或和,pre[i] 为 b 的前缀异或和。因为异或具有结合率和交换率,所以我们可以看作是你的 x 被搞事后异或上某个 (pre[i]^suf[i+1]),这样的 (pre[i]^suf[i+1]) 一共有 m+1 个。
我们以这 m+1 个数的二进制建立一棵 trie 树。然后 dfs 这棵 trie 树,如果某个节点有两个子节点:0 和 1,那么你开始选的(被搞事了的)x 这一位无论是 0 还是 1,你的对手都有选择的方法使这一位异或后变成 0. 但是如果这个节点只有一个子节点,那么聪明的你就可以做出选择,使得异或后这一位还是 1,统计答案就变得 easy 啦!
当然了,trie 树应该是每一个二进制数从前往后每一位去建立。因为你的对手想要让你选的数某一位异或后为 0,一定先考虑前面的几位,再考虑后面的几位。(因为 ∑ni=02i=2n+1−1
(应该没写错吧?)
大佬讲解,现在还没有调出来,所以先不说了
T4:
考场上唯一一道过掉的题
一眼 \(tarjan\),再一眼 \(Atm\)
然后就可以 (\(tarjan\)+XIN算法) 建 议 学 习
然后就是很套路很套路很套路的 \(tarjan\) 缩点。
只不过我的常数炸天
\(long\;long\) + \(STL\)
\(\huge{\text{险些没过}}\)
\(code\)
#include<map>
#include<cmath>
#include<queue>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
#define int long long
#define m(c,num) memset(c,num,sizeof c)
#define inf 0x7f7f7f7f
#define debug cout<<"debug"<<endl
#define freopen eat = freopen
#define scanf a14 = scanf
#define eps (1e-8)
namespace xin_io
{
#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
char buf[1<<20],*p1 = buf,*p2 = buf; FILE *eat; int a14;
inline void openfile() {freopen("t.txt","r",stdin);}
inline void outfile() {freopen("o.txt","w",stdout);}
inline int get()
{
int s = 0,f = 1;register char ch = gc();
while(!isdigit(ch))
{if(ch == '-') f = -1;ch = gc();}
while(isdigit(ch))
{s = s * 10 + ch - '0'; ch = gc();}
return s * f;
}
}
const int maxn = 1e6+10;
using namespace xin_io;
namespace xin
{
class xin_edge{public:int next,ver;} edge[maxn],edge_new[maxn];
int zhi,zhi_new,head[maxn],head_new[maxn];
void add(int u,int v) {if(u==v) return;edge[++zhi].next=head[u]; head[u]=zhi; edge[zhi].ver=v;}
void add_new(int u,int v) {edge_new[++zhi_new].next=head_new[u]; head_new[u]=zhi_new; edge_new[zhi_new].ver=v;}
int num[maxn],scc[maxn],f[maxn];
stack<int>st;
bool vis[maxn];
int n,r,c;
int scc_num,ans;
int x[maxn],y[maxn],t[maxn];
vector<int>h[maxn],s[maxn];
map<pair<int,int>,int>hash;
void build()
{
for(register int i=1;i<=r;++i)
{
register int u = 0;
for(register int j = 0;j<h[i].size();++j)
if(t[h[i][j]] == 1) {u = h[i][j]; break;}
// cout<<" i ="<<i<<endl;
for(register int j=0;j < h[i].size();++j)
{add(u,h[i][j]); if(t[h[i][j]] == 1) add(h[i][j],u);}
}
for(register int i=1;i<=c;++i)
{
register int u=0;
for(register int j=0;j<s[i].size();++j)
if(t[s[i][j]] == 2) {u = s[i][j]; break;}
for(register int j=0;j<s[i].size();++j)
{
add(u,s[i][j]);
if(t[s[i][j]] == 2) add(s[i][j],u);
}
}
int dx[8]={-1,-1,0,0,1,1,1,-1},dy[8]={1,-1,1,-1,0,1,-1,0};
for(register int i=1;i<=n;++i)
if(t[i]==3)
for(register int j=0;j<8;++j)
{
register int nowx = x[i]+dx[j],nowy = y[i]+dy[j];
if(hash[make_pair(nowx,nowy)]) add(i,hash[make_pair(nowx,nowy)]);
}
}
int dfn[maxn],low[maxn],tim = 0;
void tarjan(int x)
{
dfn[x] = low[x] = ++tim; vis[x]=1; st.push(x);
for(register int i=head[x];i;i=edge[i].next)
{
if(!dfn[edge[i].ver])
{
tarjan(edge[i].ver);
if(low[edge[i].ver]<low[x]) low[x] = low[edge[i].ver];
}
else
if(vis[edge[i].ver] and dfn[edge[i].ver]<low[x])
low[x]=dfn[edge[i].ver];
}
if(dfn[x]==low[x])
{
int temp=0; scc_num++;
while (x!=temp)
temp = st.top(),st.pop(),num[scc_num]++,vis[temp]=0,scc[temp]=scc_num;
}
}
inline void build_new_map()
{
for(register int i=1; i<=n;++i)
for(register int j=head[i]; j; j=edge[j].next)
if(scc[i]!=scc[edge[j].ver])
add_new(scc[i],scc[edge[j].ver]);//,cout<<scc[i]<<' '<<scc[edge[j].ver]<<endl;
}
void dfs(int u)
{
vis[u]=1;
for(register int i=head_new[u];i;i=edge_new[i].next)
{
if(!vis[edge_new[i].ver]) dfs(edge_new[i].ver);
f[u] = max(f[u],f[edge_new[i].ver]);
}
f[u] += num[u];
ans = max(ans,f[u]);
}
inline short main()
{
#ifndef ONLINE_JUDGE
openfile();
#endif
n = get(); r = get(); c = get();
for(register int i=1;i<=n;++i)
{
x[i] = get(),y[i] = get(),t[i] = get();
hash[make_pair(x[i],y[i])] = i;
h[x[i]].push_back(i); s[y[i]].push_back(i);
// cout<<x[i]<<' '<<y[i]<<' '<<t[i]<<endl;
}
build();
for(register int i=1;i<=n;++i)
if(!dfn[i])
tarjan(i);
build_new_map();
// cout<<scc_num<<endl;
for(register int i=1;i<=scc_num;++i)
if(!vis[i]) dfs(i);//,cout<<"f[scc_num] = "<<f[scc_num]<<endl;
printf("%lld\n",ans);
return 0;
}
}
signed main() {return xin::main();}