题解
又是一天爆零日orz
我已经尽力在概括题意啦..版权原因就不开放正式体面了!
第一题——capacitor(今天没有中文的题干)
【题目描述】
- 给出一个分数, ab a b ,给若干次操作,每次操作只能将两个数变成 c1+c2 c 1 + c 2 或者两个数变成 11c1+1c2 1 1 c 1 + 1 c 2 , c1 c 1 必为操作之后的结果, c2 c 2 必为1,问多少次操作能够做出 ab a b 。
- 给出若干组数据,每组数据为a,b两个数。
- 刚开始看样例我还以为是max(a,b),后来才发现其实是辗转相除法。就是gcd的变形。
- 每次加1的操作可以认为是 ab+1=a+bb a b + 1 = a + b b ,那么从 ab a b 进行一次操作之后的状态就变成了 a−bb a − b b
- 同理,每次进行第二次的操作,将原式变形之后,就是 ab−a a b − a 那么多少次操作就是不断的大减小,就是一个gcd的变形。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std;
inline int read(){
int X=0,w=0; char c=0;
while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
return w?-X:X;
}
void fff(){
freopen("capacitor.in","r",stdin);
freopen("capacitor.out","w",stdout);
}
LL gcd(LL a,LL b){
if(b==0) return a;
else return gcd(b,a%b);
}
int main(){
// fff();
int T;T=read();
while (T--){
LL a,b;
scanf("%lld%lld",&a,&b);
int _gcd=gcd(a,b);
a=a/_gcd,b=b/_gcd;
LL ans=0;
while (a!=0&&b!=0){
if(a==1){ans+=b;break;}
if(b==1){ans+=a;break;}
if(a==b){ans++; break;}
if(abs(a-b)==1){
ans+=max(a,b);
break;
}
if(a<b) swap(a,b);
ans+=a/b;
a=a%b;
}
printf("%lld\n",ans);
}
}
第二题——track
【题目描述】
- 给出一段长度为n的跑道,初始高度为0,位置为0,每次走一步可以向上或者向下走一步,给出一段字符串,U为往上走,D为往下走,走的高度不能小于0,在路段当中必须有一段适合字符串是匹配的走法,最终终点的高度也为0,求走法的方案数。
- 刚开始没有想全,认为只要有中间一段是合法的,那只要前面和后面的走法的方案数相乘再相加就可以了,而又有高度问题,又要枚举高度,本来以为能够骗一点分,但是没有考虑到路径中间如果有重复的走法(左右两端有走法和中间段的走法相重复),就会多解。
- 然后就想到了KMP的算法,能够很好的解决重复路径的问题,而结果为了尽可能多做贡献,则要尽量回到字符串nxt的开头,上升和下降有不同的走法,也要注意。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
inline int read(){
int X=0,w=0; char c=0;
while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
return w?-X:X;
}
void fff(){
freopen("track.in","r",stdin);
freopen("track.out","w",stdout);
}
const int MOD=1000000007;
int T,n,slen;
char s[210];
int nxt[210];
int fail[210][2];
int f[210][210][210],height[210];
void getfail(){
int j=0;
nxt[0]=nxt[1]=0;
for (int i=1;i<slen;i++){
while (j&&s[i]!=s[j]) j=nxt[j];
if(s[i]==s[j]) j++;
nxt[i+1]=j;
}
if(s[0]=='U') fail[0][0]=1;
if(s[0]=='D') fail[0][1]=1;
for (int i=1;i<=slen;i++){
int pos=i;
while(pos&&s[pos]!='U') pos=nxt[pos];
fail[i][0]=pos+1;
if(pos==0&&s[0]=='D') fail[i][0]=0;
pos=i;
while (pos&&s[pos]!='D') pos=nxt[pos];
fail[i][1]=pos+1;
if(pos==0&&s[0]=='U') fail[i][1]=0;
}
}
int main(){
// fff();
T=read();
if(T%2==1){
printf("%d\n",0);
return 0;
}
scanf("%s",&s);
slen=strlen(s);
T/=2;
getfail();
f[0][0][0]=1;
for (int i=0;i<2*T;i++){
for (int j=0;j<=T;j++){
for (int k=0;k<slen;k++){
if(s[k]=='U'){
f[i+1][j+1][k+1]=(f[i+1][j+1][k+1]+f[i][j][k])%MOD;
if(j) f[i+1][j-1][fail[k][1]]=(f[i+1][j-1][fail[k][1]]+f[i][j][k])%MOD;
}else if(s[k]=='D'){
f[i+1][j+1][fail[k][0]]=(f[i+1][j+1][fail[k][0]]+f[i][j][k])%MOD;
if(j) f[i+1][j-1][k+1]=(f[i+1][j-1][k+1]+f[i][j][k])%MOD;
}
}
f[i+1][j+1][slen]=(f[i+1][j+1][slen]+f[i][j][slen])%MOD;
if(j)f[i+1][j-1][slen]=(f[i+1][j-1][slen]+f[i][j][slen])%MOD;
}
}
int ans=f[2*T][0][slen];
printf("%d",ans);
return 0;
}
第三题——test
【这是一道模板题】
- 给出一棵原式根为1的点权树,有q步操作:
- 1、将树根的改为t,
- 2、将某个节点修改为v
- 3、求某个节点的子树和
- 4、求两点之间的距离(点权和)。
- 考场上除了树刨、树状数组、dfn序都想到了orz。
- 树刨和树状数组在求和和修改的时候效率很高,板子,不讲
- 在求子树和的时候,就只要判断当前的查询点在不在新的根和1(下称为源根)所在的路径上,如果不在,就无影响,按照正常来求,如果在,则求出sum[1]-sum[v(查询点在该条路径上的儿子)],具体自己画图体会
- 求距离就不用lca了..树刨也能求orz,也是人生第一次遇到
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
inline int read(){
int X=0,w=0; char c=0;
while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
return w?-X:X;
}
void fff(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
}
const int N=1e5+10;
vector<int>G[N];
int a[N],n,q,sum=0;
int head[N],fa[N],pch[N];
int dep[N],in[N],out[N],tim=0;
int sz[N];
void dfs1(int u,int f){
dep[u]=dep[f]+1;
fa[u]=f;
sz[u]=1;
int mxsz=0,mxid=0;
int siz=G[u].size();
for (int i=0;i<siz;i++){
int v=G[u][i];
if(v==f) continue;
dfs1(v,u);
if(sz[v]>mxsz)mxsz=sz[v],mxid=i;
}
if(mxsz) swap(G[u][mxid],G[u][0]),pch[u]=G[u][0];
}
void dfs2(int x,int f,int h){
in[x]=++tim;
head[x]=h;
bool ff=true;
int siz=G[x].size();
for (int i=0;i<siz;i++){
int y=G[x][i];
if(y==f) continue;
dfs2(y,x,ff?h:y);
ff=false;
}
out[x]=tim;
}
int t[N];
void add(int x,int v){
for (;x<=n;x+=x&-x) t[x]+=v;
}
int query(int x){
int res=0;
for (;x;x-=x&-x) res+=t[x];
return res;
}
int main(){
// fff();
n=read(),q=read();
for (int i=1;i<n;i++){
int u,v;
u=read(),v=read();
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0,1);
for (int i=1;i<=n;i++) a[i]=read(),sum+=a[i],add(in[i],a[i]);
int rt=1;
while (q--){
int op;op=read();
if(op==1) rt=read();
else if(op==2){
int x,v;
x=read(),v=read();
sum+=v-a[x];
add(in[x],v-a[x]);
a[x]=v;
}else if(op==3){
int x,ans=0;
x=read();
if(x==rt) ans=sum;
else if(in[x]<in[rt]&&out[rt]<=out[x]){
int lsth=0;
int p=rt;
while (head[p]!=head[x]) lsth=head[p],p=fa[lsth];
if(p==x) p=lsth;else p=pch[x];
ans=sum-(query(out[p])-query(in[p]-1));
}else{
ans=query(out[x])-query(in[x]-1);
}
printf("%d\n",ans);
}else{
int x,y,ans=0;
x=read(),y=read();
while (x!=y){
if(head[x]==head[y]){
if(dep[x]>dep[y]) swap(x,y);
ans+=query(in[y])-query(in[x]),y=x;
}else{
if(dep[head[x]]>dep[head[y]]) swap(x,y);
ans+=query(in[y])-query(in[head[y]]-1),y=fa[head[y]];
}
}
ans+=a[x];
printf("%d\n",ans);
}
}
}