A.解方程
题目大意:给出n个整数和x,请问这n个整数中是否存在三个数a,b,c使得ax2+bx+c=0,数字可以重复使用
简要题解:枚举a和b,判断是否存在c。
#include
#include
#include
#include
#include
#include
#include
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.割草机
题目大意:
简要题解:奇数行都是从左向右走,偶数行都是从右向左走。每一行都是从最左端的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那道题,这道题应该很容易就想出来了。