T1
模拟即可,比较水。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N number
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int n;
int a[200000],len,b[200000];
int main(){
// freopen("my.out","w",stdout);
read(n);
a[1]=1;len=1;
for(int i=2;i<=n;i++){
int tail=0,now=-1,cnt=0;
for(int j=1;j<=len;j++){
if(now!=a[j]){
if(j!=1) {b[++tail]=cnt;b[++tail]=now;}
cnt=1;now=a[j];
}
else cnt++;
}
b[++tail]=cnt;b[++tail]=now;
for(int i=1;i<=tail;i++) a[i]=b[i];len=tail;
}
for(int i=1;i<=len;i++) printf("%d",a[i]);
return 0;
}
T2
不难发现答案满足可二分性,对于区间修改,我们差分,用树状数组维护前缀和,然后二分找到第一个满足自身大于下标的地方,判断该位置左边是否满足值与下标相等即可。复杂度 (O(klog ^2n)),如果用线段树并在线段树上二分可以做到一个 (log)。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 10000010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int n,k,b[N];
ll sum[N],a[N];
struct BIT{
ll p[N];
inline ll lowbit(ll x){return x&(-x);}
inline void Add(ll w,ll val){for(int i=w;i<=n;i+=lowbit(i)) p[i]+=val;}
inline ll AskSum(ll w){ll res=0;for(int i=w;i>=1;i-=lowbit(i)) res+=1ll*p[i];return res;}
void Init(){
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
p[i]=sum[i]-sum[i-lowbit(i)];
}
}
};
BIT bit;
inline bool Check(int mid){
return bit.AskSum(mid)>mid;
}
inline int erfen(){
int l=1,r=n;
while(l<r){
int mid=(l+r)>>1;
if(Check(mid)) r=mid;
else l=mid+1;
}
return l;
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(k);read(n);
for(int i=1;i<=n;i++) read(b[i]);
for(int i=1;i<=n;i++) a[i]=b[i]-b[i-1];
a[++n]=INF;bit.Init();int w=erfen();
if(bit.AskSum(w-1)!=w-1) puts("NO");else puts("YES");
for(int i=1;i<=k-1;i++){
int l,r,val;read(l);read(r);read(val);
bit.Add(l,val);bit.Add(r+1,-val);
int w=erfen();
ll now=bit.AskSum(w-1);
if(now!=w-1||w==1) puts("NO");
else puts("YES");
}
return 0;
}
T3
分块,莫队,只要是根号复杂度就能过。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int kuailen,n,q,a[N],b[N],cnt[N],nowans,ans[N];
struct Ques{
int l,r,id,ID;
inline bool operator < (const Ques &b) const{
return (id!=b.id)?(id<b.id):((id&1)?r<b.r:r>b.r);
}
};
Ques ques[N];
inline void Add(int k){
cnt[a[k]]++;if(cnt[a[k]]&1) nowans++;else nowans--;
}
inline void Del(int k){
if(cnt[a[k]]&1) nowans--;else nowans++;cnt[a[k]]++;
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);kuailen=sqrt(n);for(int i=1;i<=n;i++){read(a[i]);b[i]=a[i];}
read(q);
for(int i=1;i<=q;i++){
read(ques[i].l);read(ques[i].r);ques[i].id=ques[i].l/kuailen;ques[i].ID=i;
}
sort(ques+1,ques+q+1);sort(b+1,b+n+1);int len=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+len+1,a[i])-b;
int l=0,r=-1;
for(int i=1;i<=q;i++){
while(l>ques[i].l) Add(--l);
while(r<ques[i].r) Add(++r);
while(l<ques[i].l) Del(l++);
while(r>ques[i].r) Del(r--);
ans[ques[i].ID]=nowans;
}
for(int i=1;i<=q;i++) printf("%d
",ans[i]);
return 0;
}
T4
看了一眼 rqy 的题解,发现 rqy 真是 nb,这个题其实就是一个结论题。
设 (f_n) 为有 (n) 个节点的二叉树个数,(g_n) 表示 (n) 个节点的 (f_n) 棵二叉树的叶子总数,则答案为:
[frac{g_n}{f_n}
]
首先,不难发现的是 (f_n) 是卡特兰数,其实 (g_n) 也有一个性质:
我们有:
[g_n=nf_{n-1}
]
- 我们考虑证明:
- 考虑所有节点数为 (n) 的二叉树去掉一个叶节点变成一棵 (n-1) 个节点的二叉树。那么因为所有节点数为 (n-1) 的二叉树能够有 (n) 种方案添加一个叶子节点变成一棵 (n) 个结点的二叉树(我们一会在证明这个结论),所以我们如果尝试分别去掉 (n) 个节点的二叉树的每一个叶子,对所有的 (n) 个节点的二叉树都这么做的话,会发现所有 (n-1) 个节点的二叉树都被算了 (n) 遍,不难发现,因为是去掉每个叶子节点,所以就有 (g_n=nf_{n-1}) 。
- 现在我们来证明:所有节点数为 (n-1) 的二叉树能够有 (n) 种方案添加一个叶子节点变成一棵 (n) 个结点的二叉树。
- 考虑到把一棵二叉树上所有的边定向,全部定向成父亲到儿子的方向,那么我们可以发现如果一个节点的出度小于 (2) 的话,就可以放上面添加 (1) 到 (2) 个叶子节点。考虑整棵树一共有 (n) 个点,加入每个点的出度为 (2) 的话总出度为 (2n),但是一共有 (n-1) 条边,那么剩下的出度为 (2n-(n-1)=n+1),也就是说可以添加 (n+1) 条个儿子。
这个规律是达标发现,然后考虑证明。需要有一定的打表能力。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N number
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
const ull mod=2148473647;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int n;
inline ll ksc(ll x,ll y,ll mod){
ll z=(ld)x/mod*y;
ll res=(ull)x*y-(ull)mod*z;
return (res%mod+mod)%mod;
}
inline int ksm(int a,int b,int mod){
int res=1;
while(b){if(b&1) res=ksc(res,a,mod);a=ksc(a,a,mod);b>>=1;}
return res;
}
signed main(){
read(n);
// cout<<n*(n+1)%mod<<endl;
// cout<<ksm(4*n-2,mod-2,mod)<<endl;
printf("%lld
",ksc(ksc(n,(n+1),mod),ksm(4*n-2,mod-2,mod),mod));
return 0;
}