D.
由于一组连续三个数的情况不会超过3个,因为如果超过3个的话,那么你可以直接归类到每个数取3个就行了
所以一个数被选中为组成连续三个数的情况不会超过6个,所以我们直接f[i][j][k]表示搜到i,且i剩余j个,(i-1)剩余k个的方案数,那么你就直接dp就行了
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
int dp[MAXN][5][3];
bool b[MAXN][5][3];
int n,m,x;
int f[MAXN];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
f[x]++;
}
for (int j=0;j<=min(f[1],4);j++) dp[1][j][0]=(f[1]-j)/3,b[1][j][0]=1;
for (int i=1;i<m;i++)
{
for (int j=0;j<=4;j++)
for (int k=0;k<=2;k++)
if (b[i][j][k])
{
for (int s=0;s<=min(min(j,k),f[i+1]);s++)
{
for (int la=0;la<=min(4,f[i+1]-s);la++)
dp[i+1][la][min(j-s,2)]=max(dp[i+1][la][min(j-s,2)],dp[i][j][k]+s+(f[i+1]-s-la)/3),b[i+1][la][min(j-s,2)]=1;
}
}
}
int ans=0;
for (int j=0;j<=4;j++)
for (int k=0;k<=2;k++)
ans=max(ans,dp[m][j][k]);
cout << ans << endl;
}
E.
题意:给你一个目前已知的序列a,然后给你一个序列b,然后你有一种操作,可以把,你可以随便执行该操作,问最后能否把a变成b
解析:我们考虑一下首尾两数是不会变的,所以我们就可以先把这个判掉
然后我们思考一下,如果我们知道这个数列的差分数列的话,那么这个数列也就确定了,所以我们把a的差分数列定位a',b的差分数列定为b',我们发现上述操作就是交换相邻两个差分数列中数的位置,所以直接排序
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int a[MAXN],b[MAXN];
int n;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++) scanf("%d",&b[i]);
if (a[1]!=b[1]||a[n]!=b[n]) {
printf("%s\n","No");
}
else {
for (int i=1;i<n;i++) a[i]=a[i+1]-a[i];
for (int i=1;i<n;i++) b[i]=b[i+1]-b[i];
sort(a+1,a+n);
sort(b+1,b+n);
for (int i=1;i<n;i++) if (a[i]!=b[i]) {
printf("%s\n","No"); return 0;
}
printf("%s\n","Yes");
}
}
F.
题意:给你一棵树,每条边有一个权值,这棵树节点满足欧拉序标号,你有一系列询问让你找到与一直点v最近的叶子节点x的距离大小,l<=x<=r,
解析:那么我们思考一下,如果已经知道了每个叶子节点到根节点的距离,那么这个东西直接用线段树维护,那么我们思考一下,如果从这个点直接到他的儿子节点的话,路径权值修改的区间是连续的,那么我们可以直接记录子树的节点个数,然后直接离线加线段树维护就行了
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
const long long inf=1e17;
struct node{
int u,to;
long long w;
}edge[MAXN<<1];
struct lalala{
int l,r,id;
};
struct data{
long long min,po;
}tree[MAXN<<2];
vector<lalala>q[MAXN];
vector<pair<int,long long> >e[MAXN];
int head[MAXN],size[MAXN];
int n,x,l,r,v,m,k;
long long w;
long long ans[MAXN];
void add(int x,int y,long long z)
{
edge[k].u=y;
edge[k].to=head[x];
edge[k].w=z;
head[x]=k++;
}
void put(int l,int r,int k,int t,long long s)
{
tree[k].min=min(tree[k].min,s);
if (l==r) return;
else {
int mid=(l+r)/2;
if (t<=mid) put(l,mid,k<<1,t,s); else put(mid+1,r,k<<1|1,t,s);
}
}
int dfs(int now,long long len)
{
bool p=0;
size[now]=1;
for (int i=0;i<e[now].size();i++)
{
int u=e[now][i].first;
p=1;
size[now]+=dfs(u,len+e[now][i].second);
}
if (!p) put(1,n,1,now,len);
return size[now];
}
void update(int k)
{
tree[k].min=min(tree[k<<1].min,tree[k<<1|1].min);
}
void down(int k)
{
tree[k<<1].min+=tree[k].po; tree[k<<1].po+=tree[k].po;
tree[k<<1|1].min+=tree[k].po; tree[k<<1|1].po+=tree[k].po;
tree[k].po=0;
}
long long findans(int l,int r,int k,int ll,int rr)
{
if (l==ll&&r==rr) return tree[k].min;
else {
int mid=(l+r)/2;
down(k);
if (rr<=mid) return findans(l,mid,k<<1,ll,rr);
else if (ll>mid) return findans(mid+1,r,k<<1|1,ll,rr);
else {
return min(findans(l,mid,k<<1,ll,mid),findans(mid+1,r,k<<1|1,mid+1,rr));
}
update(k);
}
}
void change(int l,int r,int k,int ll,int rr,long long s)
{
if (l==ll&r==rr)
{
tree[k].po+=s;
tree[k].min+=s;
}
else {
int mid=(l+r)/2;
down(k);
if (rr<=mid) change(l,mid,k<<1,ll,rr,s);
else if (ll>mid) change(mid+1,r,k<<1|1,ll,rr,s);
else {
change(l,mid,k<<1,ll,mid,s); change(mid+1,r,k<<1|1,mid+1,rr,s);
}
update(k);
}
}
void dfs1(int now)
{
for (int i=0;i<q[now].size();i++)
{
ans[q[now][i].id]=findans(1,n,1,q[now][i].l,q[now][i].r);
}
int s=now;
for (int i=0;i<e[now].size();i++)
{
int u=e[now][i].first;
change(1,n,1,s+1,s+size[u],-e[now][i].second);
change(1,n,1,1,s,e[now][i].second);
if (s+size[u]+1<=n) change(1,n,1,s+size[u]+1,n,e[now][i].second);
dfs1(u);
change(1,n,1,s+1,s+size[u],e[now][i].second);
change(1,n,1,1,s,-e[now][i].second);
if (s+size[u]+1<=n) change(1,n,1,s+size[u]+1,n,-e[now][i].second);
s+=size[u];
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
for (int i=2;i<=n;i++)
{
scanf("%d%lld",&x,&w);
e[x].push_back(make_pair(i,w));
}
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&v,&l,&r);
lalala x;
x.l=l; x.r=r; x.id=i;
q[v].push_back(x);
}
for (int i=1;i<=(n<<2);i++) tree[i].min=inf;
dfs(1,0);
dfs1(1);
for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}
G.
这道题有点毒瘤
主要是分的情况数太多了
具体就不多说了,反正我们可以把白点引出一个三点链,最后把情况化为三种
1.如果有度>3的点显然可以
2.如果有度为3的两个点经过偶数条边
3.如果有一个度为3的点其两个子树中的点的个数>1
剩余情况就是平局
(这个后手方显然不会赢,真是不知道是谁出的这道题)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5e5+10;
const int MAX=5e5+10;
vector<int>e[MAXN];
int x,T,k,y,n,s1,s2;
bool p;
char s[MAX];
void add(int x,int y)
{
e[x].push_back(y);
e[y].push_back(x);
}
int dfs(int now,int fa,int dep){
if (e[now].size()>3) p=1;
if (e[now].size()==3) {
if (dep&1) s1++; else s2++;
}
int s=0;
for (int i=0;i<e[now].size();i++)
{
int u=e[now][i];
if (e[u].size()>=2) s++;
if (u==fa) continue;
dfs(u,now,dep+1);
}
if (s>=2&&e[now].size()==3) p=1;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
s1=0; s2=0;
for (int i=1;i<n;i++){
scanf("%d%d",&x,&y);
e[x].push_back(y); e[y].push_back(x);
}
int sum=n;
p=0;
scanf("%s",s+1);
for (int i=1;i<=n;i++)
if (s[i]=='W')
{
sum++;
add(i,sum);
add(sum,sum+1);
sum++;
add(sum-1,sum+1);
sum++;
}
dfs(1,0,0);
if (p||s1>=2||s2>=2) printf("%s\n","White"); else printf("%s\n","Draw");
for (int i=1;i<=sum;i++) e[i].clear();
}
}
H.
太难了我想必是不会的