1.4651. 推导部分和 - AcWing题库
部分和——前缀和——给出两点相对关系,求要求两点相对关系——带权并查集
放在最后一题很吓人,但是实际上就是带权并查集模板题,,
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N =1e5+10;
typedef long long LL;
int n,m,q;
int p[N];
LL d[N];
int find(int x)
{
if(p[x]!=x)
{
int t=find(p[x]);
d[x]+=d[p[x]];
p[x]=t;
}
return p[x];
}
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=n;i++) p[i]=i;
while(m--)
{
int l,r;
long long s;
cin>>l>>r>>s;
int x=find(--l),y=find(r);
p[y]=x;
d[y]=s-d[r]+d[l];
}
while(q--)
{
int l,r;
cin>>l>>r;
int x=find(--l),y=find(r);
if(x!=y) puts("UNKNOWN");
else printf("%lld\n",d[r]-d[l]);
}
}
2.
如果先分解质因数,发现如果质因数的幂次是倍数关系,就可以合起来。如果合到只剩两个数,设幂次为a,b。则说明每个质因数幂次c可以被a,b线性表示。有个常识是:2 和3可以线性表示所有数。
另外,如果有个质因数幂次为1,那么一定不可能,直接结束。
因此问题变成能否被表示成:。接下来分情况讨论。
如果有个是1,即为检查n是否为平方数或者立方数。
如果都不是1,则需要继续拆分。删除1-4000中n的质因子,如果是1,直接g
不是1,则再判断是否是平方数或者立方数。
#include<iostream>
#include<algorithm>
#include<cstring>
typedef long long LL;
using namespace std;
const int N = 4010;
int primes[N],cnt;
bool st[N];
int n;
void init(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
bool check(LL x) //判断一个数是否是平方数或立方数
{
LL l = 1, r = 2e9;
while (l < r)
{
LL mid = l + r >> 1;
if (mid * mid >= x)
r = mid;
else
l = mid + 1;
}
if (l * l == x)
return true;
l = 1, r = 2e6;
while (l < r)
{
LL mid = l + r >> 1;
if (mid * mid * mid >= x)
r = mid;
else
l = mid + 1;
}
if (l * l * l == x)
return true;
return false;
}
int main()
{
int T;
cin>>T;
init(4000);
while(T--)
{
LL x;
cin>>x;
if(check(x)) puts("yes");
else
{
bool op=false;
for(int i=0;i<cnt;i++)
{
if(x%primes[i]==0)
{
int t=0;
while(x%primes[i]==0)
x/=primes[i],t++;
if(t==1)
{
op=true;
break;
}
}
}
if(!op&&check(x))
puts("yes");
else puts("no");
}
}
}
3.4646. 爬树的甲壳虫 - AcWing题库
有点意思的纯数学题,列等式求概率再求期望即可。
设Ei为从i号爬到n号点的期望。En=0
Ei=1(爬一次)+pi+1*E0(掉下去了)+(1-pi+1)Ei+1(没掉下去)
递推求E0就可。此外还有快速幂求逆元知识。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N =1e5+10,INF=1e9+10,mod=998244353;
typedef long long LL;
int a[N],b[N];
int n;
LL qmi(int a,int b,int p)
{
LL res=1;
while(b)
{
if(b&1) res=(LL)res*a%p;
b>>=1;
a=(LL)a*a%p;
}
return res;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
int res=0;
LL t=1;
for(int i=n;i>=1;i--)
{
t=t*b[i]%mod;
t=t*qmi(b[i]-a[i],mod-2,mod)%mod;
res=(res+t)%mod;
}
cout<<res;
}
4.4648. 最长不下降子序列 - AcWing题库
已知求最长不下降子序列的方法。那么答案容易想到应该是 正序n-k点前的最长下降子序列+k、逆序k点后的最长下降子序列+k、某点i的正序最长+k+(i+k)的逆序最长。
因此不能用二分法那个状态表示,必须用n方那个算法。但是题目要求nlogn,因此要某种优化。
状态转移中,是找到前面小于i号点值的点中,f(j)最大的。因此可以考虑线段树,以a[i]为下标,存储f[i]值。线段树里存最大值,
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int N = 200010;
int f1[N],f2[N];
int a[N];
int n,k,m;
vector<int> num;
struct Node{
int l, r;
int v; // 区间[l, r]中的最大值
}tr[N * 4];
void pushup(int u) // 由子节点的信息,来计算父节点的信息
{
tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
}
void build(int u, int l, int r)
{
tr[u] = {l, r};
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
int query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].v; // 树中节点,已经被完全包含在[l, r]中了
int mid = tr[u].l + tr[u].r >> 1;
int v = 0;
if (l <= mid) v = query(u << 1, l, r);
if (r > mid) v = max(v, query(u << 1 | 1, l, r));
return v;
}
void modify(int u,int x,int v)
{
if(tr[u].l==tr[u].r) tr[u].v=v;
else
{
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) modify(u<<1,x,v);
else modify(u<<1|1,x,v);
pushup(u);
}
}
int find(int x){
return lower_bound(num.begin(),num.end(),x)-num.begin();
}
int main()
{
cin>>n>>k;
num.push_back(0);
for(int i=1;i<=n;i++) cin>>a[i],num.push_back(a[i]);
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
for(int i=1;i<=n;i++) a[i]=find(a[i]);
m=num.size();
build(1,1,num.size());
int res=0;
for(int i=1;i<=n;i++)
{
f1[i]=query(1,1,a[i])+1;
modify(1,a[i],f1[i]);
}
build(1,1,m);
for(int i=n;i>=k+1;i--)
{
res=max(res,k+f1[i-k-1]+query(1,a[i-k-1],m));
f2[i]=query(1,a[i],m)+1;
modify(1,a[i],f2[i]);
}
for(int i=1;i<=n-k;i++){
res=max(res,f1[i]+k);
}
for(int i=n;i>=k+1;i--){
res=max(res,f2[i]+k);
}
cout<<res;
}
5.选数异或![](https://img-blog.csdnimg.cn/7d4e6099a996479db14526a90a9f978f.png)
异或操作是鼠鼠心理阴影捏,经常没思路。
状态表示f(r)存储的是最大的l,使得l到r内,存在两个满足条件的数。
简单来说:last记录元素出现的最后一个位置。当有一个遍历到一个新点i,根据之前有没有出现过A[i]来判断。所以
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
using namespace std;
const int N =1e5+10;
typedef long long LL;
LL a[N];
unordered_map<int,int> last;
int n,m,x;
int f[N];
int main()
{
cin>>n>>m>>x;
for(int i=1;i<=n;i++)
{
int s;
cin>>s;
f[i]=max(last[s^x],f[i-1]);
last[s]=i;
}
while(m--)
{
int l,r;
cin>>l>>r;
if(f[r]>=l) cout<<"yes"<<endl;
else cout<<"no" <<endl;
}
}