Wannafly挑战赛4 解题报告

A.解方程

题目大意:给出n个整数和x,请问这n个整数中是否存在三个数a,b,c使得ax2+bx+c=0,数字可以重复使用

简要题解:枚举a和b,判断是否存在c。

#include
#include
#include
#include
#include
#include
#include

using namespace std;

int a[1010];
int n,x;
map mp;

int main()
{
	scanf("%d%d",&n,&x);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]),mp[-a[i]]=1;
	bool w=0;
	for (int i=1;i<=n && !w;i++)
	  for (int j=1;j<=n && !w;j++)
	    if (mp[a[i]*x*x+a[j]*x]) w=1;
	if (w) puts("YES"); else puts("NO");
	return 0;
}


B.小AA的数列

题目大意:给定一个长度为n的数列,求所有长度为偶数且在[L,R]之内的区间异或值之和。

简要题解:问题可以转化为求长度为偶数且不超过R的答案。按位枚举,从前向后扫,分别记录当前奇数/偶数位置的0/1有多少个,即可计算出以当前位置为右端点的区间的答案。当长度大于R时,则删除左端点。

#include
#include
#include
#include
#include
#include
#define maxn 100010
#define mod 1000000007

using namespace std;

int a[maxn],b[2][2],c[maxn];
long long ans;
int n,L,R;

long long calc(int now,int len)
{
	memset(b,0,sizeof(b));
	long long ans=0;
	c[0]=0;for (int i=1;i<=n;i++) if (a[i]&(1ll<len) b[(i-len-1)&1][c[i-len-1]]--;
		ans+=b[i&1][!c[i]];
		b[i&1][c[i]]++;
	}
	return ans%mod;
}

long long calc(int len)
{
	long long ans=0;
	for (int i=0;i<31;i++) ans+=1ll*(1ll<


C.割草机

题目大意:

n*m的01矩阵,从(1,1)出发,面向(1,m),可以按如下两种方式移动,均消耗1单位时间:
1、向面朝的方向移动一格
2、向下移动一格,并反转面朝的方向(右变左,左变右)
求遍历所有1的最少时间

简要题解:奇数行都是从左向右走,偶数行都是从右向左走。每一行都是从最左端的1走到最右端的1,模拟一下就可以。注意处理中间的空行。

#include
#include
#include
#include
#include
#include

using namespace std;

char s[210];
int n,m,mx[210],mn[210],N;
int ans,now;

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		mx[i]=0;mn[i]=m+1;
		for (int j=1;j<=m;j++) if (s[j]=='W') mn[i]=min(mn[i],j),mx[i]=max(mx[i],j);
	}
	ans=mx[1]-1;now=mx[1];
	for (int i=1;i=1;i--) if (mx[i]


D.树的距离

题目大意:给定一棵n个节点的树,m组询问,每次询问x子树内,与x距离大于等于k的点到x的距离之和。

简要题解:巧的是和昨天CF的F题撞了,又恰巧我在比赛前的那个下午做了那道F题,于是成功拿到一血。x与x子树内的节点距离可转化为深度之差,故可以将深度离散化。按深度升序排序,建主席树,下标为dfs序,维护dep之和。本应查询的是query(root[dep[x]+k],in[x],out[x])-query(root[dep[x]-1],in[x],out[x]),但可以发现x子树内的点dep值都大于x,故后一项为0,直接询问前缀即可。(好像比原题弱化了)

#include
#include
#include
#include
#include
#include
#include
#define N 4001000
#define maxn 200010

using namespace std;

int head[maxn],len[maxn],nxt[maxn],to[maxn],root[maxn];
long long dep[maxn],b[maxn],sum[N];
int lch[N],rch[N],cnt[N];
int n,T,num,tot,in[maxn],out[maxn],m;
vector v[maxn];

void addedge(int x,int y,int z)
{
	num++;to[num]=y;len[num]=z;nxt[num]=head[x];head[x]=num;
}

void dfs(int x)
{
	in[x]=++tot;
	for (int p=head[x];p;p=nxt[p]) dep[to[p]]=dep[x]+len[p],dfs(to[p]);
	out[x]=tot;
}

int modify(int pre,int l,int r,int pos,long long d)
{
	int now=++tot;
	if (l==r) sum[now]=sum[pre]+d,cnt[now]=cnt[pre]+1;
	else
	{
		int mid=(l+r)>>1;
		if (pos<=mid)
		{
			lch[now]=modify(lch[pre],l,mid,pos,d);rch[now]=rch[pre];
		}
		else
		{
			rch[now]=modify(rch[pre],mid+1,r,pos,d);lch[now]=lch[pre];
		}
		sum[now]=sum[lch[now]]+sum[rch[now]];
		cnt[now]=cnt[lch[now]]+cnt[rch[now]];
	}
	return now;
}

long long query_sum(int x,int l,int r,int L,int R)
{
	if (L<=l && r<=R) return sum[x];
	int mid=(l+r)>>1;
	long long ans=0;
	if (L<=mid) ans+=query_sum(lch[x],l,mid,L,R);
	if (mid>1,ans=0;
	if (L<=mid) ans+=query_cnt(lch[x],l,mid,L,R);
	if (mid


E.方程的解

题目大意:T组询问,判断模p意义下方程x^2+ax+b=0是否有整数解,其中p为奇质数。

简要题解:配方后,可看做x^2=a是否有解,套板子即可。注意特判p=2的情况。

#include   
#include   
#include   
#include   
#include   
#include   
  
using namespace std;  
typedef long long LL;  
  
LL quick_mod(LL a, LL b, LL m)  
{  
    LL ans = 1;  
    a %= m;  
    while(b)  
    {  
        if(b & 1)  
        {  
            ans = ans * a % m;  
            b--;  
        }  
        b >>= 1;  
        a = a * a % m;  
    }  
    return ans;  
}  
  
struct T  
{  
    LL p, d;  
};  
  
LL w;  
  
//二次域乘法  
T multi_er(T a, T b, LL m)  
{  
    T ans;  
    ans.p = (a.p * b.p % m + a.d * b.d % m * w % m) % m;  
    ans.d = (a.p * b.d % m + a.d * b.p % m) % m;  
    return ans;  
}  
  
//二次域上快速幂  
T power(T a, LL b, LL m)  
{  
    T ans;  
    ans.p = 1;  
    ans.d = 0;  
    while(b)  
    {  
        if(b & 1)  
        {  
            ans = multi_er(ans, a, m);  
            b--;  
        }  
        b >>= 1;  
        a = multi_er(a, a, m);  
    }  
    return ans;  
}  
  
//求勒让德符号  
LL Legendre(LL a, LL p)  
{  
    return quick_mod(a, (p-1)>>1, p);  
}  
  
LL mod(LL a, LL m)  
{  
    a %= m;  
    if(a < 0) a += m;  
    return a;  
}  
  
LL Solve(LL n,LL p)  
{  
	if (n==0) return 0;
    if(p == 2) return 1;  
    if (Legendre(n, p) + 1 == p)  
        return -1;  
    LL a = -1, t;  
    while(true)  
    {  
        a = rand() % p;  
        t = a * a - n;  
        w = mod(t, p);  
        if(Legendre(w, p) + 1 == p) break;  
    }  
    T tmp;  
    tmp.p = a;  
    tmp.d = 1;  
    T ans = power(tmp, (p + 1)>>1, p);  
    return ans.p;  
}  
  
int main()  
{  
    int t;  
    scanf("%d", &t);  
    while(t--)  
    {  
        int a,b,p;  
        scanf("%d%d%d",&a,&b,&p);  
        if (p==2)
        {
        	if (a==0 && b==0) puts("Yes");
        	if (a==1 && b==0) puts("Yes");
        	if (a==0 && b==1) puts("Yes");
        	if (a==1 && b==1) puts("No");
        	continue;
        }
        int n = ((1ll*a*a-4ll*b)%p+p)%p;  
        int x = Solve(n, p);  
        if(x == -1) puts("No");
		else puts("Yes");   
    }  
    return 0;  
}  

F.线路规划

题目大意:n个点的树(树边不是通信线路),有m条通信线路,通信线路(x,y,k,w)表示x--y,fa[x]--fa[y],fa[fa[x]]--fa[fa[y]],……,x的k-1级父亲--y的k-1级父亲,这k条边,每条边的费用为m。求最大联通块大小为多少,在最大联通块大小尽量大的条件下,至少要花多少钱。

简要题解:多校2017某题与SCOI2016萌萌哒的合体版。最小生成树的思路,先按照边权升序排序,加入边。考虑如何快速合并,f[j][i]表示从i开始2^j个祖先的匹配情况,每次合并可用RMQ的思想,把大区间拆成两个重叠的区间(因为合并操作可以重叠),若可以把f[k][x]与f[k][y]合并,则把这个区间拆成2个重叠区间,merge(x,y,k-1),merge(fa[k-1][x],fa[k-1][y],k-1)。对于每一个f[j][i]最多被合并一次,所以总复杂度O(n log n),而倍增又可以快速判断两个区间是否需要合并,所以问题得到完美解决。

#include
#include
#include
#include
#include
#include
#define maxn 300010

using namespace std;

struct edge
{
	int x,y,k,w;
}e[maxn];

int head[maxn],to[maxn],nxt[maxn];
int dep[maxn],size[maxn],fa[20][maxn],f[20][maxn];
long long ans[maxn];
int n,K,m,num;

void addedge(int x,int y)
{
	num++;to[num]=y;nxt[num]=head[x];head[x]=num;
}

void dfs(int x)
{
	for (int p=head[x];p;p=nxt[p]) dep[to[p]]=dep[x]+1,dfs(to[p]);
}

int go_up(int x,int d)
{
	for (int i=0;i<=K;i++) if (d&(1<size[ANS] || (size[i]==size[ANS] && ans[i]


总结:很可惜,差两名拿到奖金。输在罚时上,真的很气。B题没有看到取模,没有删调试导致挂了3次。C题数组开小了又挂了一次。D题犯了一个很蠢的错误,挂了一次。E题没有考虑2的情况,挂了一次。应该说,除了E之外,这些错误基本都可以避免,所以拿不到奖金还是要反思的。比较开心的是,凭借下午刚码的主席树抢到了D的一血。F题是道好题,但是如果会做SCOI那道题,这道题应该很容易就想出来了。

你可能感兴趣的:(总结)