21杭电多校第一场

A

签到题,显然可以取到所有\(<\lceil\frac{n}{2}\rceil\)的所有数,则答案就是\(n-1\)的最高位\(2^x-1\)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll n,T;
int main()
{
	rep(T,1,read())
	{
		n=read();
		ll p=1;while(p<n) p*=2;
		p/=2;printf("%lld\n",max(p-1,0LL));
	}
}

B

\(kd-tree\)暴力查询,记录一下子树内的权值和,每次修改的时候由下至上更新

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,xx[MAXN],yy[MAXN],w[MAXN],id[MAXN],rr[MAXN],Dim;
inline ll sqr(ll x){return x*x;}
struct node {int mx[2],mn[2],d[2],l,r,id;ll sum,w;};
bool operator < (const node a,const node b) {return a.d[Dim]<b.d[Dim];}
struct kd_tree
{
    int Rt,fa[MAXN];node tr[MAXN],l,r;
    void upd(int k)
    {
    	l=tr[tr[k].l],r=tr[tr[k].r];
		rep(i,0,1)
        {
            if(tr[k].l)
				tr[k].mx[i]=max(tr[k].mx[i],l.mx[i]),tr[k].mn[i]=min(tr[k].mn[i],l.mn[i]);
            if(tr[k].r)
				tr[k].mx[i]=max(tr[k].mx[i],r.mx[i]),tr[k].mn[i]=min(tr[k].mn[i],r.mn[i]);
        }
    }
    int build(int l,int r,int now)
    {
        int mid=l+r>>1;Dim=now;nth_element(tr+l,tr+mid,tr+r+1);
        rep(i,0,1) tr[mid].mn[i]=tr[mid].mx[i]=tr[mid].d[i];
        id[tr[mid].id]=mid;
        if(l<mid) tr[mid].l=build(l,mid-1,now^1),fa[tr[mid].l]=mid;
        if(mid<r) tr[mid].r=build(mid+1,r,now^1),fa[tr[mid].r]=mid;
		upd(mid);return mid;
    }
    void mdf(int k,int w)
    {
		tr[k].w=w;tr[k].sum+=w;
		for(k=fa[k];k;k=fa[k]) tr[k].sum+=w;
	}
    void init()
	{
		rep(i,1,n)
		{
			tr[i].id=i,tr[i].l=tr[i].r=fa[i]=tr[i].sum=tr[i].w=0;
			xx[i]=tr[i].d[0]=read(),yy[i]=tr[i].d[1]=read();
			w[i]=read(),rr[i]=read();
		}
		Rt=build(1,n,0);
	}
	inline int ok(int cx,int cy,int R,int a,int b)
	{
		return sqr(a-cx)+sqr(b-cy)<=sqr(R);
	}
	inline int in(int cx,int cy,int R,int x1,int y1,int x2,int y2)
	{
		if(!ok(cx,cy,R,x1,y1)) return 0;
		if(!ok(cx,cy,R,x1,y2)) return 0;
		if(!ok(cx,cy,R,x2,y1)) return 0;
		if(!ok(cx,cy,R,x2,y2)) return 0;
		return 1;
	}
	inline int out(int cx,int cy,int R,int x1,int y1,int x2,int y2)
	{
        int a,b;
		if(x1<=cx&&cx<=x2) a=cx;
        else if(cx<x1) a=x1;else a=x2;
		if(y1<=cy&&cy<=y2) b=cy;
        else if(cy<y1) b=y1;else b=y2;
		if(ok(cx,cy,R,a,b)) return 0;
		else return 1;
	}
	ll query(int k,int x,int y,int R,ll res=0)
	{
		if(!tr[k].sum) return 0;
		if(in(x,y,R,tr[k].mn[0],tr[k].mn[1],tr[k].mx[0],tr[k].mx[1]))
			return tr[k].sum;
		if(out(x,y,R,tr[k].mn[0],tr[k].mn[1],tr[k].mx[0],tr[k].mx[1]))
			return 0;
		if(ok(x,y,R,tr[k].d[0],tr[k].d[1])) res=tr[k].w;
		if(tr[k].l) res+=query(tr[k].l,x,y,R);
		if(tr[k].r) res+=query(tr[k].r,x,y,R);
		return res;
	}
    void solve()
    {
    	rep(i,1,n)
		{
			printf("%lld\n",query(Rt,xx[i],yy[i],rr[i])+w[i]);
			mdf(id[i],w[i]);
		}
	}
}kd;
int main()
{
	rep(T,1,read())
		{n=read();kd.init();kd.solve();}
}

C

将每个边是否选表示为一个\(0/1\)变量,用异或方程表示限制,则答案为\(2^k\)其中\(k\)为自由元个数

则题目中所有环没有公共边只有公共点,可以表示为和一个节点相邻的边数为偶数

每个方块周围边数的奇偶性也同样可以表示

可以用\(bitset\)加速这个异或方程的高斯消元求解过程

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,tot,idx[20][20],idy[20][20],cnt,ans;
char s[20];
bitset<700> a[700];
int main()
{
	int now,pos;rep(T,1,read())
	{
		n=read()-1,m=read()-1;tot=cnt=0;ans=-1;
		rep(i,1,n+1) rep(j,1,m) idx[i][j]=++tot;
		rep(i,1,n) rep(j,1,m+1) idy[i][j]=++tot;
		rep(i,1,n+1) rep(j,1,m+1)
		{
			a[++cnt].reset();
			if(i>1) a[cnt][idy[i-1][j]]=1;
			if(j>1) a[cnt][idx[i][j-1]]=1;
			if(i<=n) a[cnt][idy[i][j]]=1;
			if(j<=m) a[cnt][idx[i][j]]=1;
		}
		rep(i,1,n)
		{
			scanf("%s",s+1);
			rep(j,1,m) if(s[j]!='.')
			{
				a[++cnt].reset();a[cnt][tot+1]=s[j]-'0';
				a[cnt][idx[i][j]]=a[cnt][idy[i][j]]=1;
				a[cnt][idx[i+1][j]]=a[cnt][idy[i][j+1]]=1;
			}
		}
		n=tot,m=cnt;now=1,pos;
		rep(i,1,n)
		{
			pos=0;rep(j,now,m) if(a[j][i]) {pos=j;break;}
			if(!pos) continue;
			if(pos!=now) swap(a[pos],a[now]);
			rep(j,now+1,m) if(a[j][i]) a[j]^=a[now];
			now++;
		}
		rep(i,now,m) if(a[i].any()) {ans=0;break;}
		if(~ans) {puts("0");continue;}
		ans=1;rep(i,now,n) ans=mul(ans,2);
		printf("%d\n",ans);
	}
}

D

\(f(i,j)\)表示前\(j\)个物品总价值为\(i\)的方案数,则有

\[f(i,j)=f(i,j-1)+f(i-a_j,j)=\sum\limits_{t=0}^{\lfloor \frac{i}{a_j}\rfloor}f(i-t\cdot a_j,j-1) \]

最好将上面的向下取整扔掉,考虑将\(i\)写成\(k\cdot LCM+r\)的形式,其中\(r=k'a_j+q\)

\[f(k\cdot LCM+r,j)=\sum\limits_{t=0}^{k\cdot LCM/a_j+k'}f(k\cdot LCM+r-t\cdot a_j,j-1) \]

可以证明\(f(k\cdot LCM+r,j)\)为关于\(k\)\(j-1\)次多项式,记为\(F_{j,r}\)\(f(k\cdot LCM+r,j)=F_{j,r}(k)\)

因此可以暴力\(dp\)出前\(n\cdot LCM\)项的\(f(i,n)\)值,复杂度\(O(n^2LCM)\)

取出余数相同的\(f\)作为\(F_{n,r}\)\(n\)个函数点值,使用拉格朗日插值即可求出\(F_{n,r}(k)\)


拉格朗日插值的构造非常暴力,设\(F(x)=\sum\limits_{i=1}^nf_i(x)\),其中\(f_i(x)\)表示只满足\((x_i,y_i)\),其余\(y|_{x=x_j}=0\)的函数

\(f_i(x)=y_i\frac{\prod_{i\neq j}x-x_j}{\prod_{i\neq j}x_i-x_j}\)即符合条件

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 300100
#define MOD 1000000007
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,a[120],lcm,inv[120],f[MAXN],y[120];ll k;
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int Lagrange(ll x,int res=0)
{
	int tmp;x%=MOD;rep(i,1,n)
	{
		tmp=1;
		rep(j,1,i-1) tmp=mul(tmp,inv[i-j]);
		rep(j,i+1,n) tmp=mul(tmp,-inv[j-i]);
		rep(j,1,n) if(i!=j) tmp=mul(tmp,x-j);
		tmp=mul(tmp,y[i]);
		res=pls(res,tmp);
	}
	return (res+MOD)%MOD;
}
int main()
{
	inv[0]=inv[1]=1;rep(i,2,105) inv[i]=mul(inv[MOD%i],(MOD-MOD/i));
	int x,g;rep(T,1,read())
	{
		n=read(),k=read();lcm=1;
		rep(i,1,n) a[i]=read(),g=gcd(lcm,a[i]),lcm=lcm/g*a[i];
		f[0]=1;rep(i,1,n*lcm) f[i]=0;
		rep(i,1,n) rep(j,a[i],n*lcm) f[j]=pls(f[j],f[j-a[i]]);
		if(k<=n*lcm) {printf("%d\n",f[k]);continue;}
		rep(i,0,n-1) y[i+1]=f[k%lcm+i*lcm];
		printf("%d\n",Lagrange(k/lcm+1));
	}
}

E

签到题,每个合数\(i\)可以向他们的约数连边,贡献为\(i\);每个素数\(p\)可以和\(2\)连边,贡献为\(2p\)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 10010010
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,p[MAXN/7],tot,ntp[MAXN];ll ans[MAXN];
void mem(int n=1e7)
{
	rep(i,2,n)
	{
		if(!ntp[i]) p[++tot]=i;
		rep(j,1,tot) if(i*p[j]>n) break;
			else {ntp[i*p[j]]=1;if(i%p[j]==0) break;}
	}
	rep(i,3,n) ans[i]=ans[i-1]+(ntp[i]?i:2*i);
}
int main()
{
	mem();
	rep(T,1,read()) printf("%lld\n",ans[read()]);
}

F

转化为前缀和后,问题变为查找最近的两个数使得他们的异或和\(\ge k\)

从左到右加入每个前缀和,对\(trie\)树上的每个节点记录经过这个点的最大编号

对每个右端点\(r\),在查询的时候按照\(k \otimes sum_r\)\(trie\)树上遍历

若该位为\(0\)\(1\)的子树内所有数都符合条件,选出最大值;其余情况就一直沿着\(trie\)树走到底再查询

每次查询后更新答案

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,k,tr[MAXN*30][2],tot,g[30],mx[MAXN*30],ansl,ansr,ans=inf;
void ins(int x,int id)
{
	int t,res=-1,p=0;dwn(i,29,0)
	{
		t=(x>>i)&1;
		if(!g[i])
		{
			if(tr[p][t^1]) {res=max(res,mx[tr[p][1^t]]);break;}
			if(tr[p][t]) p=tr[p][t],res=mx[p];
		}
		else 
		{
			if(tr[p][t^1]) p=tr[p][t^1],res=mx[p];
			else {res=-1;break;}
		}
	}
	//cout<<id<<" "<<res<<endl;
	if(~res) if(id-res<ans) ans=id-res,ansl=res+1,ansr=id;
	p=0;dwn(i,29,0)
	{
		t=(x>>i)&1;if(!tr[p][t]) tr[p][t]=++tot;
		p=tr[p][t],mx[p]=id;
	}
}
int main()
{
	rep(T,1,read())
	{
		n=read(),k=read();int s=0;tot=0;ans=inf;
		dwn(i,29,0) if(k&(1<<i)) g[i]=1;else g[i]=0;
		ins(s,0);rep(i,1,n) {s^=read();ins(s,i);}
		if(ans!=inf) printf("%d %d\n",ansl,ansr);
		else puts("-1");
		dwn(i,tot,0) tr[i][0]=tr[i][1]=0;
	}
}

G

\(f_i\)表示一共\(n\)个人的时候第\(i\)轮传回自己的方案数

由于传\(i\)次球的总方案数为\((n-1)^i\),其中\(f_i\)种对应此时球在自己手中的情况,而剩余的\((n-1)^i-f_i\)种由于再传一次可以传回自己,则这些方案和\(f_{i+1}\)形成了一一对应,即\(f_i+f_{i+1}=(n-1)^i\)

由于\(f_1=0\),可以看出\(f_i=(n-1)^{i-1}-(n-1)^{i-2}+\cdots +(-1)^{i-2}(n-1)\)

给出\(f_i\equiv t\ (mod\ P)\),即:

\[\begin{aligned} t&\equiv (-1)^{i-2}(n-1)\sum\limits_{i=0}^{i-2}[-(n-1)]^i\ (mod\ P)\\ t&\equiv (-1)^{i-2}(n-1)\frac{1-[-(n-1)]^{i-1}}{1-[-(n-1)]}\ (mod\ P)\\ \frac{nt}{n-1}&\equiv (-1)^{i-2}\{1-[-(n-1)]^{i-1} \}\ (mod\ P) \end{aligned} \]

需要对\(i\)进行奇偶性讨论分别计算

  • \(i\)为奇数时,\(\frac{nt}{n-1}\equiv -[1-(n-1)^{i-1}]\ (mod\ P)\)\(\lgroup\frac{nt}{n-1}+1\rgroup(n-1)\equiv (n-1)^i\ (mod\ P)\)

  • \(i\)为奇数时,\(\frac{nt}{n-1}\equiv 1-[-(n-1)^{i-1}]\ (mod\ P)\)\(\lgroup\frac{nt}{n-1}-1\rgroup(n-1)\equiv (n-1)^i\ (mod\ P)\)

均转化为了\(bsgs\)的形式,套板子之后求解即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct HashTable
{
	const int mod=100007;
    int fst[MAXN],key2[MAXN],nxt[MAXN],tot;
    ll key1[MAXN];
    inline void mem(){memset(fst,0,sizeof(fst));tot=0;}
    void ins(ll k1,int k2)
    {
		int hsh=(int)k1%mod;
        key1[++tot]=k1,key2[tot]=k2;
        nxt[tot]=fst[hsh],fst[hsh]=tot;
    }
    int find(ll k1)
    {
        int hsh=(int)k1%mod;
        for(int i=fst[hsh];i;i=nxt[i])
            if(key1[i]==k1) return key2[i];
        return -1;
    }
}hsh;
int qp(int x,int t,int res=1)
{
	for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);
	return res;
}
#define inv(x) qp(x,MOD-2)
int bsgs(int y,int z)
{
    int ok=0;hsh.mem();
    if(!(y%MOD)) {return -1;}
    ll m=ceil(sqrt(MOD)),a=1ll,res;
    for(int i=0;i<=m;i++)
        hsh.ins(a*z%MOD,i),(a*=y)%=MOD;
    a=1ll,res=qp(y,m);
    for(int i=1,t;i<=m;i++)
    {
        (a*=res)%=MOD,t=hsh.find(a);
        if(t!=-1) return ((m*i-t)%MOD+MOD)%MOD;
    }
    return -1;
}
int n,x,ans1,ans2,ans;
int main()
{
	int t;rep(T,1,read())
	{
		n=read(),x=read();
		x=mul(x,mul(n,inv(n-1)));
		t=mul(pls(x,1),n-1);ans1=bsgs(n-1,t);//odd
		t=mul(mns(x,1),n-1);ans2=bsgs(n-1,t);//even
		if(ans1!=-1&&ans1%2==0) ans1=-1;
		if(ans2!=-1&&(ans2&1)) ans2=-1;
		ans=min(ans1,ans2);
		if(ans==-1) ans=max(ans1,ans2);
		printf("%d\n",ans);
	}
}

H

预处理每个点向下最远不降到哪

枚举矩形横着的边,可以用单调栈处理出这一行每个点左右第一个下降距离比它小的点

则这个点的答案即为\((r-l-1)*dis_i\),每个点都更新答案即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 2020
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(register int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(register int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,g[MAXN][MAXN],mx[MAXN][MAXN],ans,l[MAXN],r[MAXN],s[MAXN],tp;
int main()
{
	rep(T,1,read())
	{
		n=read(),m=read();ans=0;
		rep(i,1,n) rep(j,1,m) g[i][j]=read(),mx[i][j]=0;
		rep(j,1,m) rep(i,1,n) if(g[i][j]>g[i+1][j]||i==n) mx[i][j]=i;
		rep(j,1,m) dwn(i,n,1) if(!mx[i][j]) mx[i][j]=mx[i+1][j];
		rep(i,1,n)
		{
			tp=0;rep(j,1,m)
			{
				while(tp&&mx[i][s[tp]]>=mx[i][j]) tp--;
				if(tp) l[j]=s[tp];else l[j]=0;s[++tp]=j;
			}
			tp=0;dwn(j,m,1)
			{
				while(tp&&mx[i][s[tp]]>=mx[i][j]) tp--;
				if(tp) r[j]=s[tp];else r[j]=m+1;s[++tp]=j;
			}
			rep(j,1,m) ans=max(ans,(r[j]-l[j]-1)*(mx[i][j]-i+1));
		}
		printf("%d\n",ans);
	}
}

I

根据题意,需要求是否存在\(D\)使得将\(\le D\)的所有边加入后, 图上有\(K\)个联通块

将边排序后,按照顺序加边并查集判断

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 500100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(register int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(register int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct Edge{int u,v,w;}e[MAXN];
bool operator < (Edge a,Edge b){return a.w<b.w;}
int n,m,k,fa[MAXN],tot,ans;
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int u,int v){int fu=find(u),fv=find(v);if(fu!=fv) fa[fu]=fv,tot--;}
int main()
{
    rep(T,1,read())
    {
        scanf("%d%d%d",&n,&m,&k);rep(i,1,n) fa[i]=i;tot=n;ans=-1;
        rep(i,1,m) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        if(n==k) {puts("0");continue;}sort(e+1,e+m+1);
        rep(i,1,m)
        {
            merge(e[i].u,e[i].v);
            if(tot<k) break;
            if((e[i].w!=e[i+1].w||i==m)&&tot==k) {ans=e[i].w;break;}
        }
        printf("%d\n",ans);
    }
}

J

莫队+树状数组暴力跑的非常快

用树状数组维护当前区间里的数,外面套莫队即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int lim=1e5;
int n,m,g[MAXN],bl[MAXN],sz,c[MAXN];
int ans[MAXN],res,w[MAXN];
struct ask{int l,r,a,b,id;}q[MAXN];
inline void mdf(int x,int w) {if(!x) return ;for(;x<=lim;x+=x&-x) c[x]+=w;}
inline int query(int x,int res=0) {if(!x) return 0;for(;x;x-=x&-x) res+=c[x];return res;}
bool cmp(ask a,ask b){return bl[a.l]<bl[b.l]||(bl[a.l]==bl[b.l]&&a.r<b.r);}
int main()
{
	rep(T,1,read())
	{
		Fill(c,0);Fill(w,0);
	    n=read(),sz=sqrt(n);m=read();rep(i,1,n) g[i]=read()+1,bl[i]=(i-1)/sz;g[n+1]=n+1;
	    rep(i,1,m) q[i].l=read(),q[i].a=read()+1,q[i].r=read(),q[i].b=read()+1,q[i].id=i;
	    sort(q+1,q+m+1,cmp);int l=0,r=0;rep(i,1,m)
	    {
	        while(l<q[i].l) {w[g[l]]--;if(w[g[l]]==0) mdf(g[l],-1);l++;}
	        while(l>q[i].l) {l--;w[g[l]]++;if(w[g[l]]==1) mdf(g[l],1);}
	        while(r>q[i].r) {w[g[r]]--;if(w[g[r]]==0) mdf(g[r],-1);r--;}
	        while(r<q[i].r) {r++;w[g[r]]++;if(w[g[r]]==1) mdf(g[r],1);}
	        ans[q[i].id]=query(q[i].b)-query(q[i].a-1);
	    }
		rep(i,1,m) printf("%lld\n",ans[i]);
	}
}

L

这个题限制比较多,考虑\(burnside\)定理,对于旋转\(i\)这个置换,求解其不动点个数

有循环节个数\(=gcd(i,n)\)即需要考虑对一个长度为\(gcd(i,n)\)的环染色的方案

\(F(i,j)\)表示长度为\(i\)的环染\(j\)个绿色且相邻颜色不同的方案数

可以先考虑将\(j\)个绿色先放在一些不相邻的位置,则剩余的连续空位显然只有两种填法

\(j=0\)的时候根据\(i\)的奇偶性可能无解或有两种情况,也很显然

\(G(i,j)\)表示长度为\(i\)的环选\(j\)个不相邻的位置的方案数,则\(F(i,j)=2^mG(n,m)\)

对于\(G(i,j)\)可以做一个简单的分类讨论

  • \(n\)位置不被选中时,考虑将所有绿色与后面的一个空位捆绑考虑,则存在\(j\)个绿色块,\(i-2j\)个空位,方案为\(\binom{i-j}{j}\)
  • 当选\(n\)时,\(1\)位置也一定不能选,可以不考虑\(1\)位置,将剩余的所有绿色与前面一个空位捆绑考虑,存在\(j-1\)个自由的绿色块,\(i-2-2(j-1)=i-2j\)个空位,方案为\(\binom{i-j-1}{j-1}\)

加和得到\(G(i,j)=\binom{i-j}{j}+\binom{i-j-1}{j-1}\)

预处理阶乘和\(2^k\)之后即可\(O(1)\)得到\(F(i,j)\)

现在考虑整个答案的计算:

因为每个循环的所有颜色应当一样,故\(j\cdot\frac{n}{gcd(n,i)}\le k\)\(j\le \lfloor \frac{k\cdot gcd(n,i)}{n}\rfloor\)则有:

\[\begin{aligned} ans&=\frac{1}{n} \sum\limits_{i=1}^n \sum_{j=0}^{\lfloor \frac{k\cdot gcd(n,i)}{n}\rfloor}F(gcd(i,n),j)\\ ans&=\frac{1}{n} \sum\limits_{d|n} \sum\limits_{i=1}^n\sum_{j=0}^{\lfloor \frac{kd}{n}\rfloor}F(d,j)[(i,n)==d]\\ ans&=\frac{1}{n} \sum\limits_{d|n} \sum_{j=0}^{\lfloor \frac{kd}{n}\rfloor}F(d,j)\sum\limits_{i=1}^n[(i/d,n/d)==1]\\ ans&=\frac{1}{n} \sum\limits_{d|n} \varphi(\frac{n}{d})\sum_{j=0}^{\lfloor \frac{kd}{n}\rfloor}F(d,j)\\ \end{aligned} \]

由于需要\(d\ge j\),否则\(F(d,j)\)没有意义,因此计算\(\sum_{j=0}^{\lfloor \frac{kd}{n}\rfloor}F(d,j)\)的时间复杂度至多为\(O(d)\)

\(\sum\limits_{d|n} d=\sum\limits_{d|n}\frac{n}{d}\le \sum\limits_{i=1}^n \frac{n}{i} =nlogn\)

故可以直接枚举\(d\),暴力计算所有\(F(d,j)\)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 1001001
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,k,ntp[MAXN],p[MAXN],tot,phi[MAXN],fac[MAXN],ifac[MAXN],pw2[MAXN],ans;
int qp(int x,int t,int res=1)
{
    for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);
    return res;
}
#define inv(x) qp(x,MOD-2)
void mem(int n=1e6)
{
    phi[1]=1;rep(i,2,n)
    {
        if(!ntp[i]) p[++tot]=i,phi[i]=i-1;
        rep(j,1,tot) if(p[j]*i>n) break;
            else
            {
                ntp[i*p[j]]=1;
                if(i%p[j]) phi[i*p[j]]=phi[i]*(p[j]-1);
                else {phi[i*p[j]]=phi[i]*p[j];break;}
            }
    }
    fac[0]=pw2[0]=1;rep(i,1,n) fac[i]=mul(fac[i-1],i),pw2[i]=mul(pw2[i-1],2);
    ifac[n]=inv(fac[n]);dwn(i,n-1,0) ifac[i]=mul(ifac[i+1],i+1);
}
inline int C(int n,int m)
{
    if(n<m||n<0||m<0) return 0;
    return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
inline int F(int n,int k)
{
    if(!k) return (n&1)?0:2;
    return mul(pw2[k],pls(C(n-k,k),C(n-k-1,k-1)));
}
inline int sum(int n,int k,int res=0)
{
    rep(i,0,min(n,k)) res=pls(res,F(n,i));
    return res;
}
int main()
{
    mem();rep(T,1,read())
    {
        n=read(),k=read();ans=0;
        rep(i,1,sqrt(n)) if(n%i==0)
        {
            ans=pls(ans,mul(phi[n/i],sum(i,k*i/n)));
            if(i*i!=n) ans=pls(ans,mul(phi[i],sum(n/i,k/i)));
        }
        printf("%d\n",mul(ans,inv(n)));
    }
}
posted @ 2021-07-30 18:54  jack_yyc  阅读(33)  评论(0编辑  收藏  举报