“统信杯” 第十七届黑龙江省大学生程序设计竞赛


前言

第一次参加ACM,发挥一般。队友对我的评价:难题比签到题做的好。。。全队8发WA,一人独占7发 (还有一发是队友在我的“指导”下WA的)
气球


题目

A. Bookshelf Filling

https://ac.nowcoder.com/acm/contest/34332/A
高为 h ≤ 1 0 6 h\le 10^6 h106 书架上有一排书,从左到右分别为 n ≤ 1 0 9 n\le 10^9 n109 本高为 a a a 的书和 m ≤ 1 0 9 m\le 10^9 m109 本高为 b b b 的书。你可以将右侧至多 m − 1 m-1 m1 本书水平放在左侧书的上方,问书架最少宽多少

(zz的自闭了90min,直接拉低队伍罚时)
显然答案可二分。
高为 a a a 的书每 b b b 本为一组,剩余的视为高为 b b b 的书,贪心判断能否放下即可
队友代码:

#include<bits/stdc++.h>
#define N 
using namespace std;
int n,m,a,b,H;
inline void Rd(int &res){
	char c;res=0;
	while(c=getchar(),c<48);
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>47);
	return;
}
inline bool check(int x){
	long long nd=n+m-x;
	nd-=1ll*(H-a)*(n/b);
	if(nd<=0)return true;
	int st=n%b,last=x-n;
	last+=st;
	nd-=1ll*(H-b)*(last/b);
	if(nd<=0)return true;
	return false;
}
int main(){
	int T;
	Rd(T);
	while(T--){
		Rd(a);Rd(b);Rd(n);Rd(m);Rd(H);
		int l=n+1,r=n+m,ans;
		while(l<=r){
			int mid=(1ll*l+r)/2;
			if(check(mid))ans=mid,r=mid-1;
			else l=mid+1;
		}
		printf("%ld\n",ans);
	}
	return 0;
}

C. Tree Division

https://ac.nowcoder.com/acm/contest/34332/C
给定一棵以 1 1 1 为根的树,每个结点有一个权值 a i a_i ai。求是否能将这棵树的点划分成两个集合 A A A B B B,使得 ∀ u , v ∈ A , u ≠ v , u \forall u,v\in A,u\neq v,u u,vA,u=v,u v v v 的祖先, a u < a v a_u<a_v au<av ∀ u , v ∈ B , u ≠ v , u \forall u,v\in B,u\neq v,u u,vB,u=v,u v v v 的祖先, a u > a v a_u>a_v au>av

可以发现,每棵子树的根节点属于 A A A,则权值为子树内 A A A 集合中最小的,否则为 B B B 集合中最大的。
d p [ x ] [ 0 / 1 ] dp[x][0/1] dp[x][0/1] 表示在 x x x 的子树内划分, x x x 号点在 A A A 中,子树中 B B B 集合中最大值的最小可能 / x /x /x 号点在 A A A 中,子树中 A A A 集合中最小值的最大可能
转移的时候看 x x x 属于哪个集合,另一个集合的最值不变,而该集合的最值一定是 x x x 的权值
队友代码:

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n;
int a[N];
int head[N],id;
struct edge{
	int to,nxt;
}E[N<<1];
inline void add_edge(int a,int b){
	E[++id]=(edge){b,head[a]};
	head[a]=id;
}
inline void Rd(int &res){
	char c;res=0;
	while(c=getchar(),c<48);
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>47);
	return;
}
int mnb[N],mxa[N];
void dfs(int x,int f){
	mnb[x]=0;mxa[x]=1e9;
	for(int i=head[x];i;i=E[i].nxt){
		int v=E[i].to;
		if(v==f)continue;
		dfs(v,x);
		if(a[v]>a[x]&&mxa[v]>a[x])mnb[x]=max(mnb[x],min(a[v],mnb[v]));
		else if(a[v]>a[x])mnb[x]=max(mnb[x],mnb[v]);
		else if(mxa[v]>a[x])mnb[x]=max(mnb[x],a[v]);
		else mnb[x]=1e9;
		if(a[v]<a[x]&&mnb[v]<a[x])mxa[x]=min(mxa[x],max(a[v],mxa[v]));
		else if(a[v]<a[x])mxa[x]=min(mxa[x],mxa[v]);
		else if(mnb[v]<a[x])mxa[x]=min(mxa[x],a[v]);
		else mxa[x]=-1;
	}
}
int main(){
	Rd(n);
	for(int i=1;i<=n;i++)Rd(a[i]);
	for(int i=1,a,b;i<n;i++){
		Rd(a);Rd(b);
		add_edge(a,b);
		add_edge(b,a);
	}
	dfs(1,0);
	if(mnb[1]>n&&mxa[1]<0)puts("NO");
	else puts("YES");
	return 0;
}

D. Collision Detector

https://ac.nowcoder.com/acm/contest/34332/D
给定三个半径为 1 1 1 的球的球心坐标(位于同一平面,给定二维坐标),问能否给第一个球一个初始速度,使其能够与第二个球相撞,且第二个球能与第三个球相撞(碰撞符号物理规律,相切不算碰撞)

没啥好说的,判断两个碰撞角度范围是否有交集即可
队友代码:(慎抄,未通过,疑似精度问题)

#include <algorithm>
#include <cstdio>
#include <cmath>

struct vec {
    double x, y;

    void read() {
        int a, b;
        scanf("%d %d", &a, &b);
        x=a;y=b;
    }

    double len() {
        return sqrt(x*x+y*y);
    }

    friend vec operator-(vec m, vec n) {
        return vec{m.x-n.x, m.y-n.y};
    }
};

const double EPS = 1e-12;

int T;

vec o1, o2, o3;

#define M_PI acos(-1)

double trim(double x) {
    while (x >= 2*M_PI) x -=2*M_PI;
    while (x < 0) x += 2*M_PI;
    return x;
}

int main() {
    // printf("%.8lf\n", atan2(1.0, 1e4)); // 1e-4
    // printf("%.8lf\n", asin(2 / vec{1.0, 1e4}.len())); // 2e-4

    scanf("%d", &T);
    while (T--) {
        o1.read();
        o2.read();
        o3.read();



        vec d1 = o2 - o1;
        vec d2 = o3 - o2;

        // 2-->3
        double r2 = atan2(d2.y, d2.x);
        double dr2 = asin(2 / d2.len());
        double cmin = trim(r2 - dr2);
        double cmax = trim(r2 + dr2);
        if (cmin > cmax && fabs(cmin-cmax)>EPS) cmin -=2*M_PI;
        // printf("%.10Lf %.10Lf\n", cmin*180/M_PI, cmax*180/M_PI);

        // 1-->2
        double r1 = atan2(d1.y, d1.x);;
        double dr1 = asin(2 / d1.len());
        double vmin = trim(r1 + dr1 - M_PI/2);
        double vmax = trim(r1 - dr1 + M_PI/2);
        if (vmin > vmax && fabs(vmin-vmax)>EPS) vmin -=2*M_PI;
        // printf("%.10Lf %.10Lf\n", vmin*180/M_PI, vmax*180/M_PI);

        if ((cmax > vmin&&fabs(cmax-vmin)>EPS) && (vmax > cmin&&fabs(vmax-cmin)>EPS)) {
            puts("yes");
            continue;
        }
        puts("no");
        continue;
    }
    return 0;
}
/*
1
0 0 1 1 0 2
*/

E. Exclusive Multiplication

https://ac.nowcoder.com/acm/contest/34332/E
f ( n ) = ∏ i = 1 m p i α i % 2 f(n)=\prod_{i=1}^mp_i^{\alpha_i\%2} f(n)=i=1mpiαi%2,其中 n = ∏ i = 1 m p i α i ( n ≤ 2 × 1 0 5 ) n=\prod_{i=1}^mp_i^{\alpha_i}(n\le 2\times 10^5) n=i=1mpiαi(n2×105)
给定数组 b ( b i ≤ 2 × 1 0 5 ) b(b_i\le 2\times 10^5) b(bi2×105),求 ∑ 1 ≤ i < j ≤ n f ( b i × b j )    m o d    ( 1 0 9 + 7 ) \sum_{1\le i<j\le n}f(b_i\times b_j) \;mod \;(10^9+7) 1i<jnf(bi×bj)mod(109+7)

显然f是积性函数
大概证明:
∵ ( n , m ) = 1 ∴ ∄ p , p ∣ n 且 p ∣ m ∴ 由 f 的 定 义 可 知 f ( n ) 与 f ( m ) 之 间 互 不 影 响 ∴ f ( n m ) = f ( n ) ⋅ f ( m ) \begin{aligned}&\because (n,m)=1\\ &\therefore \not\exist p,p|n 且p|m\\ &\therefore 由f的定义可知f(n)与f(m)之间互不影响\\&\therefore f(nm)=f(n)\cdot f(m) \end{aligned} (n,m)=1p,pnpmff(n)f(m)f(nm)=f(n)f(m)
(题解用了标准的莫比乌斯反演得到了一个很简单且复杂度显然正确的式子)
推式子:
a n s = ∑ 1 ≤ i < j ≤ n f ( b i × b j ) = 1 2 ( ∑ i = 1 n ∑ j = 1 n f ( b i × b j ) − ∑ i = 1 n f ( b i 2 ) ) \begin{aligned}ans&=\sum_{1\le i<j\le n}f(b_i\times b_j)\\&=\frac{1}{2}(\sum_{i=1}^n\sum_{j=1}^nf(b_i\times b_j)-\sum_{i=1}^ nf(b_i^2)) \end{aligned} ans=1i<jnf(bi×bj)=21(i=1nj=1nf(bi×bj)i=1nf(bi2))
显然 f ( n 2 ) = 1 f(n^2)=1 f(n2)=1 (所有因数的指数都是偶数)
a n s = 1 2 ( ∑ i = 1 n ∑ j = 1 n f ( b i × b j ) − n ) \begin{aligned}ans&=\frac{1}{2}(\sum_{i=1}^n\sum_{j=1}^nf(b_i\times b_j)-n) \end{aligned} ans=21(i=1nj=1nf(bi×bj)n)
c i c_i ci b b b 中数值 i i i 出现的次数, m m m b b b 中的最大值
a n s = 1 2 ( ∑ i = 1 m ∑ j = 1 m f ( i × j ) ⋅ c i ⋅ c j − n ) \begin{aligned}ans&=\frac{1}{2}(\sum_{i=1}^m\sum_{j=1}^mf(i\times j)\cdot c_i\cdot c_j-n) \end{aligned} ans=21(i=1mj=1mf(i×j)cicjn)
问题转化为求 ∑ i = 1 m ∑ j = 1 m f ( i j ) ⋅ c i ⋅ c j \sum_{i=1}^m\sum_{j=1}^mf(ij)\cdot c_i\cdot c_j i=1mj=1mf(ij)cicj
原 式 = ∑ i = 1 m ∑ j = 1 m ∑ ( i , j ) = d f ( i j ) ⋅ c i ⋅ c j = ∑ d = 1 m ∑ i = 1 m ∑ j = 1 m f ( i j ) ⋅ c i ⋅ c j [ ( i , j ) = d ] = ∑ d = 1 m ∑ i = 1 ⌊ m d ⌋ ∑ j = 1 ⌊ m d ⌋ f ( i j d 2 ) ⋅ c i d ⋅ c j d [ ( i , j ) = 1 ] = ∑ d = 1 m ∑ i = 1 ⌊ m d ⌋ ∑ j = 1 ⌊ m d ⌋ f ( i j ) ⋅ c i d ⋅ c j d [ ( i , j ) = 1 ] ( f 中 平 方 因 子 对 f 的 值 没 有 影 响 ) = ∑ d = 1 m ∑ i = 1 ⌊ m d ⌋ ∑ j = 1 ⌊ m d ⌋ f ( i ) ⋅ f ( j ) ⋅ c i d ⋅ c j d [ ( i , j ) = 1 ] = ∑ d = 1 m ∑ i = 1 ⌊ m d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ g ∣ ( i , j ) μ ( g ) ⋅ f ( i ) ⋅ f ( j ) ⋅ c i d ⋅ c j d = ∑ d = 1 m ∑ g = 1 ⌊ m d ⌋ μ ( g ) ∑ i = 1 ⌊ m d g ⌋ ∑ j = 1 ⌊ m d g ⌋ ⋅ f ( i g ) ⋅ f ( j g ) ⋅ c i d g ⋅ c j d g = ∑ d = 1 m ∑ g = 1 ⌊ m d ⌋ μ ( g ) ( ∑ i = 1 ⌊ m d g ⌋ f ( i g ) ⋅ c i d g ) 2 \begin{aligned}原式&=\sum_{i=1}^m\sum_{j=1}^m\sum_{(i,j)=d}f(ij)\cdot c_i\cdot c_j\\&=\sum_{d=1}^m\sum_{i=1}^m\sum_{j=1}^mf(ij)\cdot c_i\cdot c_j[(i,j)=d]\\&=\sum_{d=1}^m\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}f(ijd^2)\cdot c_{id}\cdot c_{jd}[(i,j)=1]\\&=\sum_{d=1}^m\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}f(ij)\cdot c_{id}\cdot c_{jd}[(i,j)=1](f中平方因子对f的值没有影响)\\&=\sum_{d=1}^m\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}f(i)\cdot f(j)\cdot c_{id}\cdot c_{jd}[(i,j)=1]\\&=\sum_{d=1}^m\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{g|(i,j)}\mu(g)\cdot f(i)\cdot f(j)\cdot c_{id}\cdot c_{jd}\\&=\sum_{d=1}^m\sum_{g=1}^{\lfloor\frac{m}{d}\rfloor}\mu(g)\sum_{i=1}^{\lfloor\frac{m}{dg}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{dg}\rfloor}\cdot f(ig)\cdot f(jg)\cdot c_{idg}\cdot c_{jdg}\\&=\sum_{d=1}^m\sum_{g=1}^{\lfloor\frac{m}{d}\rfloor}\mu(g)(\sum_{i=1}^{\lfloor\frac{m}{dg}\rfloor} f(ig)\cdot c_{idg})^2 \end{aligned} =i=1mj=1m(i,j)=df(ij)cicj=d=1mi=1mj=1mf(ij)cicj[(i,j)=d]=d=1mi=1dmj=1dmf(ijd2)cidcjd[(i,j)=1]=d=1mi=1dmj=1dmf(ij)cidcjd[(i,j)=1](ff)=d=1mi=1dmj=1dmf(i)f(j)cidcjd[(i,j)=1]=d=1mi=1dmj=1dmg(i,j)μ(g)f(i)f(j)cidcjd=d=1mg=1dmμ(g)i=1dgmj=1dgmf(ig)f(jg)cidgcjdg=d=1mg=1dmμ(g)(i=1dgmf(ig)cidg)2
然后三重循环算就行了,复杂度 O ( 能 过 ) O(能过) O()
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned int uint;
typedef unsigned long long ull;
const int N=200010,M=1000010,P=1e9+7;
const int inf=0x3f3f3f3f;
const int INF=0xcfcfcfcf;
const db eps=1e-9,pi=2*asin(1);

template<typename tn1,typename tn2> bool cmax(tn1 &x,tn2 y) { return x<y?x=y,true:false; }
template<typename tn1,typename tn2> bool cmin(tn1 &x,tn2 y) { return x>y?x=y,true:false; }
#define mp(x,y) make_pair(x,y)
#define pii pair<int,int>
#define pil pair<int,ll>
#define pli pair<ll,int>
#define pll pair<ll,ll>
template<typename tn1,typename tn2> tn1 ADD(tn1 x,tn2 y,tn1 p=P) { return x+y>=p?x+y-p:x+y; }
template<typename tn1,typename tn2> tn1 MINUS(tn1 x,tn2 y,tn1 p=P) { return x-y<0?x-y+p:x-y; }
#define plus(a,b) a=ADD(a,b)
#define minus(a,b) a=MINUS(a,b)
template<typename tn> void mul(tn &a,tn b,tn p=P) { a=1ll*a*b%p; }
#define mem(a,b) memset(a,b,sizeof(a))
template<typename tn>tn MUL(tn a,tn b,tn p=P) { return 1ll*a*b%p; }

bool prime[N];
int p[N],tot=0;
int mu[N],smu[N];
ll f[N],s[N];
int times[N];
void init(int n=2e5)
{
	mem(prime,true);
	prime[1]=false,mu[1]=1,f[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(prime[i]) p[++tot]=i,f[i]=i,times[i]=1,mu[i]=P-1;
		for(int j=1;j<=tot&&i*p[j]<=n;j++)
		{
			prime[p[j]*i]=false;
			if(i%p[j]==0)
			{
				mu[p[j]*i]=0;
				if(times[i]&1) f[p[j]*i]=f[i]/p[j];
				else f[p[j]*i]=f[i]*p[j];
				times[p[j]*i]=times[i]+1;
				break;
			}
			f[p[j]*i]=f[p[j]]*f[i];
			mu[p[j]*i]=P-mu[i];
			times[p[j]*i]=1;
		}
		smu[i]=(smu[i-1]+mu[i])%P;
		// smu[i]=ADD(smu[i-1],mu[i]);
	}
/*
	cerr<<"f: \n";
	for(int i=1;i<=10;i++) cerr<<f[i]<<" ";
		cerr<<"\n";
//*/
}

int n,m=2e5;
int b[N],c[N];

template<typename tn> void read(tn &n)
{
	tn s=0,flag=1;
	char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') flag=-1;
	for(;'0'<=ch&&ch<='9';ch=getchar()) s=s*10+ch-'0';
	n=s*flag;
}

int main()
{
	init();
	read(n);
	for(int i=1;i<=n;i++) read(b[i]),c[b[i]]++;

	int ans=0;
	for(int d=1;d<=m;d++)
	{
		for(int g=1;1ll*g*d<=m;g++)
		{
			s[g]=0;
			for(int k=1;1ll*k*g*d<=m;k++) s[g]=(s[g]+1ll*f[k*g]*c[k*g*d])%P;
			s[g]=1ll*mu[g]*s[g]%P*s[g]%P;
			ans=(ans+s[g])%P;
			// for(int k=1;1ll*k*g*d<=m;k++) plus(s[g],1ll*f[k*g]*c[k*g*d]%P);
			// mul(s[g],s[g]);
			// mul(s[g],mu[g]);
			// plus(ans,s[g]);
		}
	}
	ans=(ans-n+P)%P;
	ans=1ll*ans*((P+1)/2)%P;
	// minus(ans,n);
	// mul(ans,(P+1)/2);
	printf("%d\n",ans);
	return 0;
}

F. 342 and Xiangqi

https://ac.nowcoder.com/acm/contest/34332/F
给两个中国象棋中的象(相)的初始位置,求最少走几步能使两个象(相)到达目标位置

跑个最短路 xjb乱搞都行

#include <algorithm>
#include <cstdio>
#include <cstring>

#define REP(i, n) for (int i = 1; i <= n; i++)

const int inf = 0x3f3f3f3f;

int dis[65][65];

int mov[][2] = {
    {1, 2},
    {1, 3},
    {2, 4},
    {3, 4},
    {4, 5},
    {4, 6},
    {5, 7},
    {6, 7},
    {0},
};

int T;
int a1, b1, a2, b2;

int enc(int i, int j) {
    if (i < j) return i * 8 + j;
    else return j * 8 + i;
}

int main() {
    memset(dis, inf, sizeof dis);

    REP(i, 7) REP(j, 7) {
        if (i == j) continue;
        for (int t = 0; mov[t][0] != 0; t++) {
            if (mov[t][0] == i) {
                dis[enc(i, j)][enc(mov[t][1], j)] = 1;
                // printf("%d %d -> %d %d\n", i, j, mov[t][1], j);
            }
            if (mov[t][0] == j) {
                dis[enc(i, j)][enc(i, mov[t][1])] = 1;
                // printf("%d %d -> %d %d\n", i, j, mov[t][1], i);
            }
        }
    }

    REP(x, 7) REP(y, 7) REP(u, 7) REP(v, 7) {
        if (!(x < y && u < v)) continue;
        int i = enc(x, y);
        int j = enc(u, v);
        dis[i][i] = 0;
        int x = std::min(dis[i][j], dis[j][i]);
        dis[i][j] = x;
        dis[j][i] = x;
        if (x == 1) {
            // printf("%d %d %d %d\n", i / 8, i % 8, j / 8, j % 8);
        }
    }

    for (int k = 1; k <= 64; k++) {
        for (int i = 1; i <= 64; i++) {
            for (int j = 1; j <= 64; j++) {
                if (dis[i][j] > dis[i][k] + dis[k][j]) {
                    dis[i][j] = dis[i][k] + dis[k][j];
                }
            }
        }
    }

    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d%d", &a1, &b1, &a2, &b2);
        printf("%d\n", dis[enc(a1, b1)][enc(a2, b2)]);
    }
    return 0;
}

G. Chevonne’s Necklace

https://ac.nowcoder.com/acm/contest/34332/G
有一个环,上面有 n ( n ≤ 2000 ) n(n\le 2000) n(n2000) 个珠子,每个珠子有一个权值 c i ( 0 ≤ c i ≤ 2000 ) c_i(0\le c_i\le 2000) ci0ci2000,每次可以挑选一个珠子 i i i,删去 i i i 及其后面 c i − 1 c_i-1 ci1 个珠子(共 c i c_i ci 个),求至多删去多少个珠子及方案数。两个方案视为不同当且仅当选择的珠子的集合不同

引理:对于任意一个选定的珠子集合,如果所有珠子的 ∑ c i ≤ n \sum c_i\le n cin,则一定存在一个合法的删除顺序
证明:显然 考虑一定存在一个点,选它删除的点中没有选择的点,将它删除。重复该过程直至选择的点全都删除即可
通过引理可将问题转化成 0 / 1 0/1 0/1 背包
队友代码:

#include<bits/stdc++.h>
#define N 2005
#define mod 998244353
using namespace std;
int n;
int a[N<<1];
long long dp[N];
inline void Rd(int &res){
	char c;res=0;
	while(c=getchar(),c<48);
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>47);
	return;
}
inline void add(long long &a,long long b){
	a+=b;
	if(a>=mod)a-=mod;
	else if(a<0)a+=mod;
}
int main(){
	Rd(n);
	for(int i=1;i<=n;i++)Rd(a[i]);
	dp[0]=1;
	for(int i=1;i<=n;i++)if(a[i]){
		for(int j=n;j>=a[i];j--){
			add(dp[j],dp[j-a[i]]);
		}
	}
	int mx=0;
	for(int i=n;i>=0;i--)if(dp[i]){
		mx=i;break;
	}
	printf("%d %lld\n",mx,dp[mx]);
	return 0;
}
/*
6
0 1 1 3 3 1
*/

H. Kanbun

https://ac.nowcoder.com/acm/contest/34332/H
给一个含有“-”的括号序列,按规则朗读这个括号序列,规则是遇到“(”先读括号内的以及后括号再读“(”

模拟即可
队友代码:

#include<bits/stdc++.h>
#define N 100005
#define miaomiaomiao Paulliant
using namespace std;
int n;
char str[N];
int now;
inline void Rd(int &res){
	char c;res=0;
	while(c=getchar(),c<48);
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>47);
	return;
}
int ans[N],cnt;
void dfs(){
	for(;now<=n;){
		if(str[now]=='('){
			int h=now;
			now++;
			dfs();
			ans[++cnt]=h;
		}
		else if(str[now]=='-')ans[++cnt]=now++;
		else {
			ans[++cnt]=now++;
			return;
		}
	}
}
int main(){
	Rd(n);
	scanf("%s",str+1);
	now=1;
	dfs();
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);puts("");
	return 0;
} 

I. Equal Sum Arrays

https://ac.nowcoder.com/acm/contest/34332/I
n ( n ≤ 20 ) n(n\le 20) n(n20) 的不同的正整数划分个数

直接暴力即可
队友代码:

#include<bits/stdc++.h>
#define N 
using namespace std;
int n;
bool mark[233];
inline void Rd(int &res){
	char c;res=0;
	while(c=getchar(),c<48);
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>47);
	return;
}
int sum,ans;
void dfs(int d){
	if(sum==n){
		ans++;
		return;
	}
	int ed=n-sum;
	for(int i=1;i<=ed;i++){
		sum+=i;
		dfs(d+1);
		sum-=i;
	}
}
int main(){
	Rd(n);
	dfs(1);
	printf("%d\n",ans);
	return 0;
} 

这么暴力的代码怎么能配得上这么优美的题面呢?
考虑划分集合的大小,设大小为 m m m,即划分成 m m m 个数
划分方案数即为方程 ∑ i = 1 m x i = n \sum_{i=1}^mx_i=n i=1mxi=n 的正整数解的个数
由插板法易得方程解的个数为 C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1
总方案数为 ∑ m = 1 n C n − 1 m − 1 = ∑ i = 0 n − 1 C n − 1 i = 2 n − 1 \sum_{m=1}^nC_{n-1}^{m-1}=\sum_{i=0}^{n-1}C_{n-1}^i=2^{n-1} m=1nCn1m1=i=0n1Cn1i=2n1
代码:

#include<bits/stdc++.h>
using namespace std;

int quickmi(int x,int n)
{
    int res=1;
    for(;n;n>>=1)
    {
        if(n&1) res*=x;
        x*=x;
    }
    return res;
}

int main()
{
    int k;
    scanf("%d",&k);
    printf("%d\n",quickmi(2,k-1));
    return 0;
}

L. Let’s Swap

https://ac.nowcoder.com/acm/contest/34332/L
给定两个等长字符串 S S S T T T,给定两个位置 l 1 l_1 l1 l 2 l_2 l2,对一个位置进行操作即以该位置将串分成前后两部分,两部分各自翻转。求对 S S S l 1 l_1 l1 l 2 l_2 l2 位置进行若干次操作,能否变成 T T T

显然 l 1 l_1 l1 l 2 l_2 l2 操作相间进行,且无论如何进行操作, S S S 进行循环移位后仍是 S S S S T S^T ST
不妨设 l 1 < l 2 l_1<l_2 l1<l2,观察进行 l 1 l_1 l1 l 2 l_2 l2 两次操作后 1 1 1 号位字符的位置:
1 ⟶ l 1 + 1 ⟶ ( l 2 + 1 ) − l 1 = ( l 2 − l 1 ) + 1 1\longrightarrow l_1+1\longrightarrow (l_2+1)-l_1=(l_2-l_1)+1 1l1+1(l2+1)l1=(l2l1)+1
1 1 1 号位字符向右平移了 l 2 − l 1 l_2-l_1 l2l1 格,也就是说在正向串(能够循环移位变成 S S S )中, 1 1 1 号位字符的位置只能是 g c d ( l 2 − l 1 , ∣ S ∣ ) gcd(l_2-l_1,|S|) gcd(l2l1,S) 的倍数 + 1 +1 +1 ( u p d a t e : 2022.5.24 ) (update: 2022.5.24) (update:2022.5.24)
枚举 1 1 1 号字符的位置, h a s h hash hash 判断即可
反向串则可以先对 S S S 进行一次 l 2 l_2 l2 操作
队友代码:(做法并不一样)

#include<bits/stdc++.h>
#define N 500005
#define mod 998244353
using namespace std;
int n;
int m1,m2;
char s[N],t[N],str[N];
inline void Rd(int &res){
	char c;res=0;
	while(c=getchar(),c<48);
	do res=(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>47);
	return;
}
inline long long fastpower(long long a,long long b){
	long long res=1;
	while(b){
		if(b&1)res=res*a%mod;
		a=a*a%mod;b>>=1;
	}
	return res;
}
long long sum[N];
long long inf[N],fac[N];
inline long long Calc(int l,int r){
	return (sum[r]-sum[l-1]+mod)*inf[l-1]%mod;
}
inline int NUM(int st,int mid){
	if(st<mid)return mid-st+1;
	else return mid+(n-mid-(st-mid)+1);
}
inline bool check(int x1,int x2){
	long long res=0;
	for(int i=1;i<=n;i++)res=(res+(t[i]-'a')*fac[i-1])%mod;
	for(int i=1;i<=n;i++)sum[i]=(sum[i-1]+(s[i]-'a')*fac[i-1])%mod;
	if(sum[n]==res)return true;
//	printf("%lld\n",res);
	int st=1;
	for(int i=1;i<2*n;i++){
		st=NUM(st,x1);st=NUM(st,x2);
		int o=n-st+1;
		long long R=Calc(o+1,n),L=Calc(1,o);
		long long h=(R+L*fac[st-1])%mod;
		h=(h+mod)%mod;
//		if(i==1)printf("!!%lld %lld\n",L,R);
		if(h==res)return true;
	}
	return false;
}
inline bool solve(int m1,int m2){
	for(int i=1;i<=n;i++)s[i]=str[i];
	if(check(m1,m2))return true;
	for(int i=1;i<=m1/2;i++)swap(s[i],s[m1-i+1]);
	for(int l=m1+1,r=n;l<r;l++,r--)swap(s[l],s[r]);
	return check(m2,m1);
}
int main(){
	inf[1]=fastpower(26ll,mod-2);
	fac[1]=26;
	inf[0]=fac[0]=1;
	for(int i=2;i<=500000;i++){
		inf[i]=inf[i-1]*inf[1]%mod;
		fac[i]=fac[i-1]*26%mod;
	}
	int T;
	Rd(T);
	while(T--){
		scanf("%s",str+1);
		scanf("%s",t+1);
		n=strlen(str+1);
		Rd(m1);Rd(m2);
//		solve(m1,m2);
		if(solve(m1,m2)||solve(m2,m1))puts("yes");
		else puts("no");
	}
	return 0;
}
/*
3
ljhelloh
hellohlj
4 2
thisisastr
htrtsasisi
3 5
abcde
bcdea
1 4
*/

( u p d a t e : 2022.5.24 ) (update: 2022.5.24) (update:2022.5.24)
按照自己的做法写的代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=500010,M=1000010,P=1e9+7;
const int p1=998244353,p2=1e9+9;
const int inf=0x3f3f3f3f;
const int INF=0xcfcfcfcf;
const db eps=1e-9,pi=2*asin(1);
template<typename tn> void read(tn &n);
template<typename tn1,typename tn2> bool cmax(tn1 &x,tn2 y) { return x<y?x=y,true:false; }
template<typename tn1,typename tn2> bool cmin(tn1 &x,tn2 y) { return x>y?x=y,true:false; }
#define mp(x,y) make_pair(x,y)
#define pii pair<int,int>
#define pil pair<int,ll>
#define pli pair<ll,int>
#define pll pair<ll,ll>
int ADD(int x,int y,int p=P) { return x+y>=p?x+y-p:x+y; }
int MINUS(int x,int y,int p=P) { return x-y<0?x-y+p:x-y; }
#define plus(a,b) a=ADD(a,b)
#define minus(a,b) a=MINUS(a,b)
#define mul(a,b) a=1ll*(a)*(b)%P
#define mem(a,b) memset(a,b,sizeof(a))

const int BASE=29;
int m1[N],m2[N];
void init(int n=500000)
{
	m1[0]=m2[0]=1;
	for(int i=1;i<=n;i++) m1[i]=1ll*m1[i-1]*BASE%p1,m2[i]=1ll*m2[i-1]*BASE%p2;
}

struct HASH {
	char s[N];
	int h1[N],h2[N];
	void init()
	{
		int l=strlen(s+1);
		for(int i=1;i<=l;i++)
		{
			h1[i]=(1ll*h1[i-1]*BASE+s[i]-'a'+1)%p1;
			h2[i]=(1ll*h2[i-1]*BASE+s[i]-'a'+1)%p2;
		}
	}
	pii get_hash(int l,int r)
	{
		return mp(MINUS(h1[r],1ll*h1[l-1]*m1[r-l+1]%p1,p1),MINUS(h2[r],1ll*h2[l-1]*m2[r-l+1]%p2,p2));
	}
} s,t;

bool solve(int l1,int l2)
{
	int l=strlen(s.s+1);
	int gcd=__gcd(l2-l1,l);
	for(int i=1;i<=l;i+=gcd)
		if(s.get_hash(1,l-i+1)==t.get_hash(i,l)&&s.get_hash(l-i+2,l)==t.get_hash(1,i-1)) return true;
	return false;
}

template<typename tn> void read(tn &n)
{
	tn s=0,flag=1;
	char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') flag=-1;
	for(;'0'<=ch&&ch<='9';ch=getchar()) s=s*10+ch-'0';
	if(ch=='.')
	{
		ch=getchar();
		tn r=0,R=1;
		for(;'0'<=ch&&ch<='9';ch=getchar()) r=r*10+ch-'0',R*=10;
		s+=r/R;
	}
	n=s*flag;
}

int main()
{
	init();
	int T; read(T);
	while(T--)
	{
		scanf("%s",s.s+1);
		scanf("%s",t.s+1);
		s.init(),t.init();
		int l=strlen(s.s+1);
		int l1,l2; read(l1),read(l2);
		if(l1>l2) swap(l1,l2);

		if(solve(l1,l2))
		{
			printf("yes\n");
			continue;
		}
		for(int i=1;i<=l2/2;i++) swap(s.s[i],s.s[l2-i+1]);
		for(int i=1;i<=(l-l2)/2;i++) swap(s.s[l2+i],s.s[l2+(l-l2)-i+1]);
		s.init();
		if(solve(l1,l2)) printf("yes\n");
		else printf("no\n");
	}
	return 0;
}
/*
1
ewyoizxkasn
ywensakxzio
3 8

yes
*/
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值