2022 ICPC 网络预选赛(9.25)

真容易颓。

E 构造一个序列\(a_1\)已经确定 使得\((a_i,a_{i-1})=1,a_i>1\) 求整个序列最大值。
容易知道\(a_2\)是与\(a_1\)互质的最小质数 若是2接下填3,2,3,2,3即可.若不是2填 2,3,2,3即可。

J 先后手每次从当前序列两端取走一个数字使得取出的数字严格递增取不出的人输 求谁赢。
明显取数为两端一个递增序列 先比较一下a1,an 若相等则答案固定。
若a1>an 则考虑取a1能否必胜不能则必须取an,这样递归到下一个子问题。总层数O(n).
注意两个最大值在同一端点的情况。

code
#include<cstdio>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rep(a,b,w) for(int w=a;w<=b;++w)
#define add(x,y) (x=(x+(y))>=jt?x+(y)-jt:x+(y));
#define mul(x,y) (x=(x-(y))<0?x-(y)+jt:x-(y));
using namespace std;
#define zz (now << 1)
#define yy (now << 1 | 1)
#define SZ(x) ((int)(x).size())
typedef long long ll;
const int maxn=100010;
int n,L,R;
int a[maxn],op=0;
inline void put()
{
	if(op)puts("Alice");
	else puts("Bob");
}
inline void solve(int l,int r)
{
	op^=1;
	if(l>L)
	{
		if(L==R)put();
		else op^=1;put();
		exit(0);
	}
	if(r<R)
	{
		if(L==R)put();
		else op^=1;put();
		exit(0);
	}
	if(a[l]==a[r])
	{
		if((L-l+1)&1)
		{
			put();
			exit(0);
		}
		if((r-R+1)&1)
		{
			put();
			exit(0);
		}
		op^=1;
		put();
		exit(0);
	}
	if(a[l]>a[r])
	{
		if((L-l+1)&1)
		{
			put();
			exit(0);
		}
		else solve(l,r-1);
	}
	else
	{
		if((r-R+1)&1)
		{
			put();
			exit(0);
		}
		else solve(l+1,r);
	}
}
int main()
{
	//freopen("1.in","r",stdin);
	scanf("%d",&n);
	rep(1,n,i)scanf("%d",&a[i]);
	L=1;
	rep(1,n,i)if(a[i+1]>a[i])L=i+1;
	else break;
	R=n;
	while(a[R-1]>a[R])--R;
	solve(1,n);
	return 0;
}


A 题意较为繁琐。

由于p没有2,5 本题是一个欧拉定理裸题。质数模p-1 具有循环节抽出来即可。

B 考虑删除操作 可以贪心进行(不知道咋整感觉很对) 对于改变数字大小跟删除类似实际上贪心此时不行。

进一步发现到了第k轮 若2k>=n 则答案唯一。这启示我们删除和改变数字大小本质上相同。

考虑dp删除数字此时改变即删除 设f[i][j]表示到了第i个数且第i个数强制不删已经删了j个数的最大值。复杂度n^3.

H 一棵树上选出一个集合 贡献是其中任意两个个点之间若无其他点贡献出他们只之间的边数 最后是 边数乘以集合大小为贡献。\(n\le 1000000\)

直接算显然不合适。考虑每一条边的单独贡献也不行。考虑单独拿出一条路径来计算,他们之间不允许再有关键点。

枚举这条路径长度为d 那么还可以再选择 n-d-1个点 贡献为 \(d\cdot (\sum_{i=0}^{n-d-1}(i+2)C(n-d-1,i))\)

显然有一个组合等式\(iC(n,i)=nC(n-1,i-1)\) 上述式子可以化简为\(d\cdot (n-d+3)2^{n-d-2}\)

其实就是求树上距离为d的点对个数 不过这个d取值为1~n 点分治可以做特定的d 复杂度nlogn

这个题需要使用点分治+NTT nlog^2过不了。

考虑使用树形dp来求这个东西 先求出每个点子树内的答案 再进行换根dp。

直接树形dp时 设所求为 \(dw2^c\) 从tn到x d要加1 w要减1 c要减1

\((d+1)(w-1)2^c\cdot \frac{1}{2}\) 再展开 \((dw2^c+-d2^c+w2^c-2^c)\frac{1}{2}\)

这样就能转移了 实际上还需要维护\(w2^c,d2^c\)这两个变量和上面一样写出来变化展开即可。

换根和上面本质一样 注意保存一些需要保存的东西。

code
#include<cstdio>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rep(a,b,w) for(int w=a;w<=b;++w)
#define add(x,y) (x=(x+(y))>=mod?x+(y)-mod:x+(y))
#define mul(x,y) (x=(x-(y))<0?x-(y)+mod:x-(y))
#define add2(x,y) ((x)+(y)>=mod?((x)+(y)-mod):(x+y))
#define mul2(x,y) ((x)-(y)<0?((x)-(y)+mod):(x-y))
using namespace std;
#define mod 998244353
#define ll long long
const int maxn=1000010;
#define INV 499122177ll
int n,len;
int lin[maxn],ver[maxn<<1],nex[maxn<<1],wc1,dc1,dwc1;
int m[maxn],c[maxn],wc[maxn],dc[maxn],dwc[maxn];
int ck[maxn],wck[maxn],dck[maxn],dwck[maxn];
int fdc[maxn],fc[maxn],fwc[maxn],fdwc[maxn];
inline void add1(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
inline void dfs(int x,int fa)
{
	int ww=0;
	for(int i=lin[x];i;i=nex[i])
	{
		int tn=ver[i];
		if(tn==fa)continue;
		dfs(tn,x);
		//2^c
		ww=INV*c[tn]%mod;
		add(ww,m[n-3]);
		add(c[x],ww);
		ck[tn]=ww;
		//w*2^c
		ww=mul2(wc[tn],c[tn])*INV%mod;
		add(ww,wc1);add(wc[x],ww);
		wck[tn]=ww;
		//d*2^c
		ww=add2(dc[tn],c[tn]);
		ww=INV*ww%mod;add(ww,dc1);
		add(dc[x],ww);
		dck[tn]=ww;
		//dw2^c
		ww=mul2(dwc[tn],dc[tn]);
		add(ww,wc[tn]);mul(ww,c[tn]);
		ww=ww*INV%mod;add(ww,dwc1);
		add(dwc[x],ww);
		dwck[tn]=ww;
	}
}
inline void dp(int x,int fa)
{
	int ww;
	for(int i=lin[x];i;i=nex[i])
	{
		int tn=ver[i];
		if(tn==fa)continue;
		//fc
		ww=mul2(fc[x],ck[tn]);
		int fcc=ww;
		ww=ww*INV%mod;
		add(ww,m[n-3]);
		add(fc[tn],ww);
		add(fc[tn],c[tn]);
		//wc
		ww=mul2(fwc[x],wck[tn]);
		int fwcc=ww;
		mul(ww,fcc);
		ww=ww*INV%mod;
		add(ww,wc1);
		add(fwc[tn],ww);
		add(fwc[tn],wc[tn]);
		//dc
		ww=mul2(fdc[x],dck[tn]);
		int fdcc=ww;
		add(ww,fcc);
		ww=ww*INV%mod;
		add(ww,dc1);
		add(fdc[tn],ww);
		add(fdc[tn],dc[tn]);
		//dwc
		ww=mul2(fdwc[x],dwck[tn]);
		mul(ww,fcc);
		add(ww,fwcc);
		mul(ww,fdcc);
		//cout<<ww<<' '<<"ww"<<endl;
		ww=ww*INV%mod;
		//cout<<ww<<endl;
		add(ww,dwc1);
		add(fdwc[tn],ww);
		add(fdwc[tn],dwc[tn]);
		dp(tn,x);
	}
}
int main()
{
	//freopen("1.in","r",stdin);
	scanf("%d",&n);
	m[1]=2;m[0]=1;
	rep(2,n,i)
	{
		int x,y;
		scanf("%d",&x);
		scanf("%d",&y);
		add1(x,y);add1(y,x);
		m[i]=m[i-1];add(m[i],m[i-1]);
	}
	if(n==2)
	{
		printf("2\n");
		return 0;
	}
	wc1=(ll)(n+2)*m[n-3]%mod;
	dc1=m[n-3];
	dwc1=wc1;
	dfs(1,0);
	fdwc[1]=dwc[1];fdc[1]=dc[1];fc[1]=c[1];fwc[1]=wc[1];
	dp(1,0);
	int ans=0;
	rep(1,n,i)add(ans,fdwc[i]);
	printf("%d\n",ans*INV%mod);
	//cout<<INV*8<<endl;
	//cout<<INV*99<<endl;
	return 0;
}
我也没想到这个换根会这么妙。

F 初始节点1 每个时间过后会生长k个节点。求树上两点x,y LCA.

容易想到树高只有log层每次两个节点向上跳即可。注意由于数字较大容易爆long long 需要使用除法。

code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<utility>
#define mk make_pair
#define rep(a,b,k) for(ll k=a;k<=b;++k)
#define ll long long
using namespace std;
const ll MAXN=1010;
ll T;
ll k,x,y,cnt;
ll a[MAXN];
signed main()
{
	freopen("1.in","r",stdin);
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld%lld%lld",&k,&x,&y);
		if(x==y){printf("%lld\n",x);continue;}
		ll mx=max(x,y);
		ll sum=1;cnt=0;
		while(1)
		{
			a[++cnt]=sum;
			if(mx%(1+k)==0)
			{	if(sum>=mx/(1+k))break;}
			else if(sum>mx/(1+k))break;
			sum*=(1+k);
		}
		while(cnt)
		{
			if(x>a[cnt])
			{
				ll res=x-a[cnt]-1;
				res/=k;x=res+1;
				
			}
			if(y>a[cnt])
			{
				ll res=y-a[cnt]-1;
				res/=k;y=res+1;
			}
			if(x==y)break;
			--cnt;
		}
		printf("%lld\n",x);
	}
	return 0;
}
G 给出若干个不相交的区间要求区间的max-min=r-l 求整个序列为排列的方案数。

容易想到是建树然后dp。建树可以考虑排序后利用dfs建树。

在每一层dp 要解决的其实是一个计数问题即一个序列上选出若干个连续区间,仔细思考直接计数相当困难。

考虑分组的情况给每个组一个标号这个标号会形成一个新的排列再将标号展开形成刚才的排列。

就是不考虑分配数字而是考虑分配相对位置。

code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<utility>
#include<bits/stdc++.h>
#define mk make_pair
#define ll long long
#define mod 1000000007
#define rep(a,b,k) for(ll k=a;k<=b;++k)
using namespace std;
const int MAXN=1000010;
int n,m,len,cnt;
int f[MAXN],fac[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1];
struct wy
{
	int l,r;
}s[MAXN];
inline int cmp(wy a,wy b)
{
	return a.l==b.l?a.r>b.r:a.l<b.l;
}
inline void add(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
inline void dfs(int x,int l,int r)
{
	while(s[cnt].l<=r)
	{
		if(cnt==m+1)return;
        add(x,cnt);
		++cnt;
		if(cnt==m+1)return;
		dfs(cnt-1,s[cnt-1].l,s[cnt-1].r);
	}
}
inline void dp(int x)
{
	f[x]=1;
	int sum=0;
	for(int i=lin[x];i;i=nex[i])
	{
		int tn=ver[i];
		dp(tn);
		f[x]=(ll)f[x]*f[tn]%mod;
		sum+=s[tn].r-s[tn].l;
	}
	sum=s[x].r-s[x].l+1-sum;
	f[x]=(ll)f[x]*fac[sum]%mod;
}
int main()
{
	//freopen("1.in","r",stdin);
	scanf("%d",&n);scanf("%d",&m);
	rep(1,m,i)
	{
		scanf("%d",&s[i].l);scanf("%d",&s[i].r);
	}
	fac[0]=1;
	rep(1,n,i)fac[i]=(ll)fac[i-1]*i%mod;
	sort(s+1,s+1+m,cmp);
	int rt=m+1;
	if(s[1].l==1&&s[1].r==n)cnt=2;
	else cnt=1;
	if(cnt!=m+1)dfs(rt,1,n);
	s[rt].l=1;s[rt].r=n;
	dp(rt);
	printf("%d\n",f[rt]);
	return 0;
}

K 给出一些圆radius=1和正方形边长=2 都在格点上 求perimeter.

先考虑圆 一个格子要么被两个相对的圆弧填满或被小正方形填满,此外如果这个格子只有一个圆弧是1/2派 俩就1/3派

之后数边长 原本写了一个极丑的的ban边的做法一直wa 后来想了一下一条边不被使用当且仅左边格子是黑的右边也是 或者左边有圆弧右边也有圆弧。

对每条边都判断一下这个东西就行了。

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define P 1000000007ll
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-10
#define sq sqrt
#define a(x) t[x].a
#define sum(x) t[x].sum
#define b(x) t[x].b
#define F first
#define S second
#define mod 998244353
using namespace std;
char *fs,*ft,buf[1<<15];
inline char gc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
const int MAXN=1010;
int n;
int p[225][225][9];
int vis1[225][225],vis2[225][225],vis[225][255];
int c[5];
int ans,c2,c3,ans1;
inline int ksm(int b,int p)
{
	int cnt=1;
	while(p)
	{
		if(p&1)cnt=(ll)cnt*b%mod;
		b=(ll)b*b%mod;p=p>>1;
	}
	return cnt;
}
int main()
{
	freopen("1.in","r",stdin);
	n=read();
	c2=ksm(2,mod-2);
	c3=ksm(3,mod-2);
	
	rep(1,n,i)
	{
		int op,x,y,xx,yy;
		op=read();x=read()+110;y=read()+110;
		if(op==1)
		{
			xx=x-1;yy=y+1;p[xx][yy][1]=1;vis[xx][yy]=1;
			++xx;p[xx][yy][2]=1;vis[xx][yy]=1;
			--yy;p[xx][yy][3]=1;vis[xx][yy]=1;
			--xx;p[xx][yy][4]=1;vis[xx][yy]=1;
			xx=x-1;yy=y-1;
			rep(xx,xx+1,j)rep(yy,yy+2,k)vis1[j][k]=1;
			++yy;
			rep(xx,xx+2,j)rep(yy,yy+1,k)vis2[j][k]=1;
		}
		else
		{
			vis2[x][y+1]=1;
			vis2[x][y]=1;
			vis1[x-1][y]=1;
			vis1[x][y]=1;
			xx=x-1;yy=y+1;p[xx][yy][7]=1;
			++xx;p[xx][yy][8]=1;
			--yy;p[xx][yy][5]=1;
			--xx;p[xx][yy][6]=1;
		}
	}
	rep(1,220,i)rep(1,220,j)
	{
		if(vis[i][j])continue;
		else
		{
			if(p[i][j][5]==0&&p[i][j][6]==0&&p[i][j][7]==0&&p[i][j][8]==0)continue;
			if(p[i][j][5]&&p[i][j][7]){vis[i][j]=1;continue;}
			if(p[i][j][6]&&p[i][j][8]){vis[i][j]=1;continue;}
			int w=0;
			rep(1,4,k)if(p[i][j][k+4])++w;
			if(w==2)ans1=(ans1+c3)%mod;
			if(w==1)ans1=(ans1+c2)%mod;
		}
	}
	//cout<<vis2[111][112]<<endl;
	rep(1,220,i)rep(1,220,j)
	{
		if(vis1[i][j]==1)
		{
			int cnt1=vis[i][j+1];
			cnt1=cnt1|p[i][j+1][8]|p[i][j+1][7];
			int cnt2=vis1[i][j-1]&&vis[i][j];
			cnt2=cnt2|p[i][j][5]|p[i][j][6];
			if(cnt1&&cnt2);
			else ++ans;
		}
		if(vis2[i][j]==1)
		{
			int cnt1=vis[i-1][j]==1;
			cnt1=cnt1|p[i-1][j][6]|p[i-1][j][7];
			int cnt2=vis2[i+1][j]==1&&vis[i][j]==1;
			cnt2=cnt2|p[i][j][5]|p[i][j][8];
			if(cnt1&&cnt2);
			else 
			{
				++ans;
				//cout<<i-110<<' '<<j-110<<endl;
			}
		}
	}
	printf("%d %d\n",ans,ans1);
	return 0;
}

还剩下D I C 偷懒。

posted @ 2022-09-28 19:00  chdy  阅读(200)  评论(0编辑  收藏  举报