第九场
也是打的很烂,赛时签到场
签到:A,K略过
I:
赛时python苦苦的写未过(因为我根本就不会python)当然事实上可能跟python没关系。
高精度题但是可以用__int128写(能不写高精还是尽量别)。不过由于int128这个类型并不实际存在(好像是特定编译器可用的)所以要自己写输入输出就是了。
所以说二分开平方就可以实现省略小数且不损失精度的效果了,妙。
#include<bits/stdc++.h>
using namespace std;
#define ls(x) (x<<1)
#define rs(x) ((x<<1)+1)
#define int long long
#define i128 __int128
const int mod=998244353;
const int maxn=2e5+10;
int n,a[maxn],b[15],num[maxn],dp[maxn];
vector<int>E[maxn];
struct Node{
int w=0,mx=0,lz=0;
}t[maxn<<2];
void add(int &x,int y){
x=(x+y)%mod;
}
void push_up(int x){
t[x].mx=max(t[ls(x)].mx,t[rs(x)].mx);t[x].w=0;
if(t[x].mx==t[ls(x)].mx)add(t[x].w,t[ls(x)].w);
if(t[x].mx==t[rs(x)].mx)add(t[x].w,t[rs(x)].w);
}
void push_down(int x){
t[ls(x)].mx+=t[x].lz;t[rs(x)].mx+=t[x].lz;
t[ls(x)].lz+=t[x].lz;t[rs(x)].lz+=t[x].lz;
t[x].lz=0;
}
void upd(int x,int l,int r,int q,int k){
if(l==r){
t[x].w=k;
return;
}
int mid=(l+r)>>1;
push_down(x);
if(q<=mid)upd(ls(x),l,mid,q,k);
else upd(rs(x),mid+1,r,q,k);
push_up(x);
}
void upd2(int x,int ql,int qr,int l,int r,int k){
// if (x==1) cout<<ql<<" "<<qr<<" "<<k<<endl;
if(l>=ql&&r<=qr){
t[x].mx+=k;
t[x].lz+=k;
return;
}
int mid=(l+r)>>1;
push_down(x);
if(ql<=mid)upd2(ls(x),ql,qr,l,mid,k);
if(qr>mid) upd2(rs(x),ql,qr,mid+1,r,k);
push_up(x);
}
int que(int x,int ql,int qr,int l,int r){
if(l>=ql&&r<=qr){
if(t[x].mx==0)return t[x].w;
return 0;
}
int mid=(l+r)>>1;
push_down(x);
int res=0;
if(ql<=mid)res+=que(ls(x),ql,qr,l,mid);
if(qr>mid) res+=que(rs(x),ql,qr,mid+1,r),res%=mod;
return res;
}
void solve(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
E[a[i]].push_back(i);
num[i]=E[a[i]].size();
}
for(int i=1;i<=m;i++)cin>>b[i];
sort(b+1,b+m+1);
dp[0]=1;upd(1,0,n,0,1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(num[i]-b[j]==0)upd2(1,0,E[a[i]][0]-1,0,n,-1);
if(num[i]-b[j]>0)upd2(1,E[a[i]][num[i]-b[j]-1],E[a[i]][num[i]-b[j]]-1,0,n,-1);
}
num[i]=num[i]-1;
for(int j=1;j<=m;j++){
if(num[i]-b[j]==0)upd2(1,0,E[a[i]][0]-1,0,n,1);
if(num[i]-b[j]>0)upd2(1,E[a[i]][num[i]-b[j]-1],E[a[i]][num[i]-b[j]]-1,0,n,1);
}
dp[i]=que(1,0,n,0,i-1);
// cout<<dp[i]<<endl;
upd(1,0,n,i,dp[i]);
}
cout<<dp[n]<<"\n";
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int tt;tt=1;while(tt--){
solve();
}
}
H:
计算几何,凸包+旋转卡壳,不会晚点补
B:
解法同第七场d题是最值线段树,只不过这题套了一个简单dp,补完7d就会了哈哈。但是写题之后发现自己对线段树确实没啥理解。
最值线段树更新的要点主要就是不会重置叶子结点的值并且根据最值情况来维护w。
#include<bits/stdc++.h>
using namespace std;
#define ls(x) (x<<1)
#define rs(x) ((x<<1)+1)
#define int long long
#define i128 __int128
const int mod=998244353;
const int maxn=2e5+10;
int n,a[maxn],b[15],num[maxn],dp[maxn];
vector<int>E[maxn];
struct Node{
int w=0,mx=0,lz=0;
}t[maxn<<2];
void add(int &x,int y){
x=(x+y)%mod;
}
void push_up(int x){
t[x].mx=max(t[ls(x)].mx,t[rs(x)].mx);t[x].w=0;
if(t[x].mx==t[ls(x)].mx)add(t[x].w,t[ls(x)].w);
if(t[x].mx==t[rs(x)].mx)add(t[x].w,t[rs(x)].w);
}
void push_down(int x){
t[ls(x)].mx+=t[x].lz;t[rs(x)].mx+=t[x].lz;
t[ls(x)].lz+=t[x].lz;t[rs(x)].lz+=t[x].lz;
t[x].lz=0;
}
void upd(int x,int l,int r,int q,int k){
if(l==r){
t[x].w=k;
return;
}
int mid=(l+r)>>1;
push_down(x);
if(q<=mid)upd(ls(x),l,mid,q,k);
else upd(rs(x),mid+1,r,q,k);
push_up(x);
}
void upd2(int x,int ql,int qr,int l,int r,int k){
// if (x==1) cout<<ql<<" "<<qr<<" "<<k<<endl;
if(l>=ql&&r<=qr){
t[x].mx+=k;
t[x].lz+=k;
return;
}
int mid=(l+r)>>1;
push_down(x);
if(ql<=mid)upd2(ls(x),ql,qr,l,mid,k);
if(qr>mid) upd2(rs(x),ql,qr,mid+1,r,k);
push_up(x);
}
int que(int x,int ql,int qr,int l,int r){
if(l>=ql&&r<=qr){
if(t[x].mx==0)return t[x].w;
return 0;
}
int mid=(l+r)>>1;
push_down(x);
int res=0;
if(ql<=mid)res+=que(ls(x),ql,qr,l,mid);
if(qr>mid) res+=que(rs(x),ql,qr,mid+1,r),res%=mod;
return res;
}
void solve(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
E[a[i]].push_back(i);
num[i]=E[a[i]].size();
}
for(int i=1;i<=m;i++)cin>>b[i];
sort(b+1,b+m+1);
dp[0]=1;upd(1,0,n,0,1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(num[i]-b[j]==0)upd2(1,0,E[a[i]][0]-1,0,n,-1);
if(num[i]-b[j]>0)upd2(1,E[a[i]][num[i]-b[j]-1],E[a[i]][num[i]-b[j]]-1,0,n,-1);
}
num[i]=num[i]-1;
for(int j=1;j<=m;j++){
if(num[i]-b[j]==0)upd2(1,0,E[a[i]][0]-1,0,n,1);
if(num[i]-b[j]>0)upd2(1,E[a[i]][num[i]-b[j]-1],E[a[i]][num[i]-b[j]]-1,0,n,1);
}
dp[i]=que(1,0,n,0,i-1);
// cout<<dp[i]<<endl;
upd(1,0,n,i,dp[i]);
}
cout<<dp[n]<<"\n";
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int tt;tt=1;while(tt--){
solve();
}
}
C:
首先试图打表1e5大失败(小丑)。然后我们发现修改后每个点的值应该为\(r[i]*c[j]*gcd(i,j)\),假设gcd(i,j)全为1,那么整个矩阵的和值就是\((r[1]+r[2]+...+r[n])(c[1]+c[2]+..+c[n])\)。也就是说,对于值全为1
的n*n矩阵在进行行列乘法的时候是可以直接计算出ans的。
于是就想到可以把整个矩阵拆分为若干a[i][j]相等的小矩阵进行计算,列式为\(ans=\sum_{i=1}^{n}\sum_{j=1}^{n}gcd(i,j)*c[i]*r[j]\)。
推出这个结论以后就是一个比较板的,可以用欧拉函数(特解)或是莫比乌斯反演(通解)写的一个题。(代码显然是欧拉函数
已知若\(gcd(i,j)=d\),则\(gcd(i,j)=\sum\phi(d)\)。
\(ans=\sum_{i=1}^{n}\sum_{j=1}^{n}(\sum\phi(d))*c[i]*r[j]\)
由于我们发现\(\sum_i^nc[i]*\sum_j^nr[j]=\sum_{i=1}^{n}\sum_{j=1}^{n}c[i]*r[j]\),所以可以快速转化式子
对于所有含gcd的题目都应该把gcd提前,则\(ans=\sum_{d=1}^n\phi(d)*\sum_{i=1}^{n/d}c[i]*\sum_{j=1}^{n/d}r[j]\)
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int maxn=1e5+10;
int phi[maxn],p[maxn],pw,ans,sc[maxn],sr[maxn],ctc[maxn],ctr[maxn];
vector<int>d[maxn];
bool vis[maxn];
void add(int &x,int y){
x=(x+y)%mod;
}
void getPhi() {
phi[1]=1;
for(int i=2;i<maxn;i++){
if(!phi[i]){
phi[i]=i-1;
p[++pw]=i;
}
for(int j=1;j<=pw&&p[j]*i<maxn;j++){
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
getPhi();
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
ctc[i]=1;ctr[i]=1;
for(int j=i;j<=n;j+=i){
d[j].push_back(i);
add(sc[i],1);
add(sr[i],1);
}
add(ans,1ll*phi[i]*sc[i]%mod*sr[i]%mod);
}
while(m--){
char c;int x,y;
cin>>c>>x>>y;
for(int i:d[x]){
add(ans,mod-1ll*phi[i]*sc[i]%mod*sr[i]%mod);
add(sc[i],mod-ctc[x]);
add(sr[i],mod-ctr[x]);
}
if(c=='R')ctr[x]=1ll*ctr[x]*y%mod;
else ctc[x]=1ll*ctc[x]*y%mod;
for(int i:d[x]){
add(sc[i],ctc[x]);
add(sr[i],ctr[x]);
add(ans,1ll*phi[i]*sc[i]%mod*sr[i]%mod);
}
cout<<ans<<"\n";
}
}
F:
虽然是树剖但是由于我对树剖的浅薄理解并不会。
G:
看不懂题目
其实GFD感觉都超出我水平(金牌题,故后面再说吧,银牌已经补完

浙公网安备 33010602011771号