T1
分析:
其中d(重要程度)一定是单调不升的,
所以对于选的顺序一定是按顺序来的,除非有一段相等
然后就乱搞就行了
code by wzxbeliever:
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1e5+5;
int n,cnt,last,sum,up;
int ans[maxn];
int a[maxn];
int main(){
scanf("%d",&n);
for(ri i=1;i<=n;i++)scanf("%d",&a[i]);
int now=1;
while(a[now]!=n)now++;
ans[++cnt]=now;
last=n-now+1;
for(ri i=now+1;i<=n;i++){
int pos;
if(a[i]!=last-1){
pos=i;
while(a[pos]!=last-1)pos++;
ans[++cnt]=pos-i+1;
last=a[i];
i=pos;
}
else ans[++cnt]=1,last=a[i];
}
sum=cnt;up=sum+1;
for(ri i=cnt;i>=1;i--){
printf("%d ",sum);sum--;
for(ri j=2;j<=ans[i];j++)
printf("%d ",up),up++;
}
return 0;
}
T2
分析:
考试的时候想到用并查集,但就是不知道怎么合并
对于每个询问,转化为询问动物还存活的概率
因为赢得情况很多(石头>剪刀>布>石头),但赢的概率是一定的
如果它在主场就有2/3的概率获胜,
而如果它在客场就只有1/3的概率获胜
假如现在我们要合并(u,v)两个笼子
u是主场,v是客场
假如动物j,以前就有概率存活在u,且概率为x,那么合并后它还存活的概率就位2x/3
同理客场为x/3
考虑用并查集维护信息
合并的时候要将u中所有的都乘上2/3
将v中所有的都乘上1/3
具体就是再打一个标记
合并和路径压缩的时候就对应的修改
code by std:
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
const int N=210000;
const int mo=998244353;
const int w1=(mo+1)/3;
int n,m,father[N],w[N];
int quick(int k1,int k2){
int k3=1;
while (k2){
if (k2&1) k3=1ll*k3*k1%mo;
k2>>=1; k1=1ll*k1*k1%mo;
}
return k3;
}
int merge(int k1,int k2){
father[k1]=k2; w[k1]=1ll*w[k1]*quick(w[k2],mo-2)%mo;
}
int findfather(int k1){
if (father[k1]==k1) return k1;
int f=findfather(father[k1]);
if (f!=father[k1]) w[k1]=1ll*w[k1]*w[father[k1]]%mo;
return father[k1]=f;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) father[i]=i,w[i]=1;
int tot=1;
for (int i=1;i<=n;i++) tot=3ll*tot%mo;
for (int i=1;i<=m;i++){
int ty,k1,k2;
scanf("%d%d",&ty,&k1);
if (ty==1){
scanf("%d",&k2);
k1=findfather(k1); k2=findfather(k2);
w[k1]=2ll*w[k1]*w1%mo; w[k2]=1ll*w[k2]*w1%mo;
merge(k1,k2);
} else {
int k2=findfather(k1);
int ans=1ll*tot*w[k1]%mo;
if (k1!=k2) ans=1ll*ans*w[k2]%mo;
printf("%d
",ans);
}
}
return 0;
}
结论题:
求树的最大独立集有一个贪心的做法.
每轮选择所有叶子,将它们加入答案,并将这些叶子和它们的父亲删掉****,进入下一轮.
于是只需要判断上一次的树的根节点是被删掉的,还是被它的某些儿子去掉的.
code by std:
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N=110000,mo=998244353;
struct bian{
int next,point;
}b[N<<1];
int p[N],len,n,m;
void ade(int k1,int k2){
b[++len]=(bian){p[k1],k2}; p[k1]=len;
}
void add(int k1,int k2){
ade(k1,k2); ade(k2,k1);
}
int f[N],num,u[N];
void dfs(int k1,int k2){
f[k1]=1;
for (int i=p[k1];i;i=b[i].next){
int j=b[i].point;
if (j!=k2){
dfs(j,k1); f[k1]&=(1-f[j]);
}
}
num+=f[k1];
}
void dfs1(int k1,int k2){
int tot=0;
for (int i=p[k1];i;i=b[i].next){
int j=b[i].point;
if (j!=k2) tot+=f[j];
}
for (int i=p[k1];i;i=b[i].next){
int j=b[i].point;
if (j!=k2){
if (u[k1]) u[j]=0; else u[j]=(tot==f[j]);
dfs1(j,k1);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++){
int k1,k2; scanf("%d%d",&k1,&k2);
add(k1,k2);
}
dfs(1,0); dfs1(1,0);
int ans=num; printf("%d
",ans); int tag=f[1];
for (;m;m--){
int k1; scanf("%d",&k1);
ans=1ll*ans*n%mo;
if (tag==0){
ans=(ans+num)%mo; tag=f[k1]&&(u[k1]==0);
} else tag=0;
printf("%d
",ans);
}
}