contest题解
B
题意
一棵树,每个节点一个齿轮,相邻节点联动(方向相反)。有三种操作
- 删除一个节点上的齿轮,使得周围的齿轮不连通
- 复原一个齿轮
- 旋转一个齿轮
每次旋转操作输出总旋转角度,并且最后求每个齿轮的角度和
一道数据结构好题!
首先对于方向,可以看成抑或,所以到根差分一下,深度为奇数的点所有操作和最后结果取负
删除和加入对于度数很大的节点难以处理,因为是一棵树,想到和fa联系起来
找到和当前点x相连的最浅的祖先y。直接对y的子树进行修改。所有的修改把贡献进行差分。
具体来说,
一个点的角度=它的修改量-最近的被拿走的祖先的多余修改量
最后一次性加入所有被删除的点,使得多余修改量被加回来。
一个点和其子树的联通块大小=sz[x] - 子树中所有被删除的联通块之和
注意每次加入和删除的时候都要和y差分,保证维护的答案符合性质
怎么求y?
链剖,每个链维护一个set,二分即可
这道题整整写了3个小时:30min想细节,60min写代码,1h20min调题,非常糟糕!
总结所有的错误:
差分没有完全想清楚,一开始删除点的lastval求错
找y的时候在链上的标号和点标号混了,然后如果是轻儿子往上跳的特判写错了
打错了线段树和其他一个字符
一开始觉得代码复杂有些不敢写,写完发现其实逻辑非常清楚,但是细节注意的不够好。必须做到零错误,才能成为合格的数据结构选手!
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;
const ld inf = 2e18;
const int N = 3e6 + 10;
const int maxn = 100020;
const ll mod = 1e9 + 7;
inline ll power(ll x,ll y){
y = ((y % (mod - 1)) + (mod - 1)) % (mod - 1);
// if ( y < 0 ) return power(power(x,-y),mod - 2);
ll res = 1;
while ( y ){
if ( y & 1 ) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
struct node{
int next,to;
}e[maxn * 2];
int head[maxn],cnt;
int lastval[maxn],tag[maxn];
int n;
//=======================lian pou======================================================
int fa[maxn],son[maxn],top[maxn],sz[maxn],dth[maxn],dfstime,id[maxn],l[maxn],r[maxn];
set <pr> v[maxn];
void dfs(int x){
sz[x] = 1;
fore(i,x){
if ( e[i].to == fa[x] ) continue;
fa[e[i].to] = x , dth[e[i].to] = dth[x] + 1;
dfs(e[i].to);
sz[x] += sz[e[i].to];
if ( sz[son[x]] < sz[e[i].to] ) son[x] = e[i].to;
}
}
void dfs2(int x){
l[x] = ++dfstime;
if ( son[x] ){
top[son[x]] = top[x] , id[son[x]] = id[x] + 1;
dfs2(son[x]);
}
fore(i,x){
if ( son[x] == e[i].to || fa[x] == e[i].to ) continue;
top[e[i].to] = e[i].to , id[e[i].to] = 1;
dfs2(e[i].to);
}
r[x] = dfstime;
}
inline void adde(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
void init(){
dth[1] = 2 , id[1] = 1;
dfs(1) , top[1] = 1, dfs2(1);
}
//========================================================================
namespace Seg{
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
const int M = 1e5 + 20;
int add[M << 2],val[M << 2],s[M << 2];
inline void Add(int x,int d){
val[x] += d , add[x] += d;
}
inline void pushdown(int x){
if ( add[x] ){
Add(ls(x),add[x]);
Add(rs(x),add[x]);
add[x] = 0;
}
}
void modify(int x,int l,int r,int L,int R,int d){
// if ( x == 1 ) cout<<L<<" "<<R<<" "<<d<<endl;
if ( L <= l && R >= r ) { Add(x,d); return; }
int mid = (l + r) >> 1;
pushdown(x);
if ( L <= mid ) modify(ls(x),l,mid,L,R,d);
if ( R > mid ) modify(rs(x),mid + 1,r,L,R,d);
//update(x);
}
inline void update(int x){
s[x] = s[ls(x)] + s[rs(x)];
}
void modify(int x,int l,int r,int id,int d){
if ( l == r ){
s[x] += d;
return;
}
int mid = (l + r) >> 1;
if ( id <= mid ) modify(ls(x),l,mid,id,d);
else modify(rs(x),mid + 1,r,id,d);
update(x);
}
int query(int x,int l,int r,int L,int R){
if ( L > R ) return 0;
if ( L <= l && R >= r ) return s[x];
int mid = (l + r) >> 1; int res = 0;
if ( L <= mid ) res = query(ls(x),l,mid,L,R);
if ( R > mid ) res += query(rs(x),mid + 1,r,L,R);
return res;
}
int query(int x,int l,int r,int id){
if ( l == r ) return val[x];
int mid = (l + r) >> 1;
pushdown(x);
if ( id <= mid ) return query(ls(x),l,mid,id);
return query(rs(x),mid + 1,r,id);
}
}
using namespace Seg;
inline int findNotTake(int x){
int res = 0;
while ( x ){
int y = top[x];
auto it = v[y].upper_bound(mp(id[x],x));
if ( it == v[y].begin() ){
res = top[x] , x = fa[top[x]];
continue;
}
y = (*(--it)).se;
if ( fa[res] == y ) return res;
return son[y];
}
return 1;
}
void rotate(int x,int a){
int p = findNotTake(x),tp = (dth[x] & 1) ? -1 : 1;
// cout<<x<<" "<<p<<" "<<a<<endl;;
modify(1,1,n,l[p],r[p],a * tp);
printf("%d\n",a * (sz[p] - query(1,1,n,l[p],r[p])));
}
void takeOut(int x){
int p = findNotTake(x),q = fa[p];
int curs = sz[x] - query(1,1,n,l[x],r[x]);
modify(1,1,n,l[x],curs);
lastval[x] = query(1,1,n,l[x]);
if ( q ){
modify(1,1,n,l[q],-curs);
lastval[x] += lastval[q] - query(1,1,n,l[q]);
}
v[top[x]].insert(mp(id[x],x));
tag[x] = 1;
}
void putIn(int x){
v[top[x]].erase(mp(id[x],x));
int p = findNotTake(x);
int q = fa[p],curs = query(1,1,n,l[x],l[x]);
int curv = query(1,1,n,l[x]);
tag[x] = 0;
if ( !q ){
modify(1,1,n,l[x],-curs);
modify(1,1,n,l[x],r[x],lastval[x] - curv);
return;
}
modify(1,1,n,l[x],-curs);
modify(1,1,n,l[q],curs);
int vq = lastval[q] - query(1,1,n,l[q]);
modify(1,1,n,l[x],r[x],lastval[x] - curv - vq); //x 的val = 第一个被删除的点的delta + 真实值
}
int main(){
// freopen("input.txt","r",stdin);
scanf("%d",&n);
rep(i,1,n - 1){
int x,y;
scanf("%d %d",&x,&y);
adde(x,y) , adde(y,x);
}
init();
int q;
scanf("%d",&q);
// rep(i,1,n) cout<<l[i]<<" ";
// cout<<endl;
while ( q-- ){
int tp , x , a;
scanf("%d",&tp);
if ( tp == 1 ) {
scanf("%d",&x);
takeOut(x);
}
else if ( tp == 2 ){
scanf("%d",&x);
putIn(x);
}
else{
scanf("%d %d",&x,&a);
rotate(x,a);
}
}
rep(i,1,n){
if ( tag[i] ) putIn(i);
}
int sum = 0;
rep(i,1,n){
// cout<<(query(1,1,n,l[i]) * ((dth[i] & 1) ? -1 : 1) % 360 + 360) % 360<<endl;
sum += (query(1,1,n,l[i]) * ((dth[i] & 1) ? -1 : 1) % 360 + 360) % 360;
}
cout<<sum<<endl;
}
C
构造最小长度的包含K个长度为n的不同子串的字符串,用m的字符集。 K <= min(MN,100000)
结论:
可以用长度为n + k - 1 的串构造,恰好同时是上下界
证明过程:
打表发现可行。
然后构造的时候发现可以把长度为n - 1的串当成点,最后一个字符看成边,构成一张有向图,这张图每个点恰好有m条出边和入边,所以一定存在欧拉回路。而欧拉回路的每条边代表一个字符串。
但是这还只是开始:
当n很大的时候,图的点数是MN,必须动态开点。
然后我就犯了很sb的错误:欧拉回路跑一半return。这样显然是错的。因为如果只跑一班,会导致环不完整,即不该相邻的点相邻。----这东西样例都过不了,但是最后调试的时候太慌,没有静心思考为什么错了。导致最后半个小时都在无意义修改。
但是我们发现这个欧拉回路是可以调整的:lyk的解决错误的方法的确很妙,我总是草率的觉得一个算法有问题就肯定不行,其实可以改进!
我们观察后发现欧拉回路的不完整的只有一个环,并且不完整的欧拉回路一定恰好有一个出度>入度的点。
我们可以把这个点当作起点,先跑完整的环,最后加入不完整的部分,于是我们得到一条合法的欧拉路径,就可以作为答案
细节:
因为每个点代表的串长度很大,要用hash或者string,动态开点。并且要记录这个点是什么需要在dfs的时候开一个数组记录边。
找欧拉路的时候,找最后一个点作为起点是不同的,要区分开。
最后欧拉路是几段路径拼起来,注意顺序。
这道题最后还是对拍才发现错误----没有区分入度>出度的点和出度>入度的点。思考得不够仔细!
题解给出的做法:
在n>40的时候随机----这是一个很好的思路,虽然这道题用不上。
n小的时候用Hierholzer’s algorithm求欧拉回路
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for (int i = l ; i <= (int)r ; i++)
#define repd(i,r,l) for (int i = r ; i >= l; i --)
#define fore(i,x) for (int i = head[x]; i ; i = e[i].next)
#define rvc(i,S) for (int i = 0 ; i < (int)S.size() ; i++)
#define pb push_back
typedef long long ll;
typedef long long ull;
const int maxn = 2000020;
const int inf = 1e8;
ull base = 907;
ull mod = 1e9 + 7;
int n,m,k;
map <ull,int> num;
int vis[maxn][12];
int ans[maxn],tot;
int cnt,b[maxn],cnt2,c[20],z[20],deg[maxn];
ull a[maxn],pow_[maxn];
int st[20],id,ans2[maxn],tot2,flag;
ull gethash(ull cur,int i,int last){
cur = (cur - last * pow_[n - 2] % mod + mod) % mod;
cur = (cur * base + i) % mod;
return cur;
}
void dfs(int x){
// cout<<x<<" : ";
// repd(i,n - 1,1) cout<<b[cnt2 - i + 1] + 1;
// cout<<endl;
k--;
if ( k == -1 ){
flag = 1;
return;
}
// if ( tot >= k ) return;
rep(i,0,m - 1){
if ( vis[x][i] ) continue;
// cout<<x<<" "<<i<<endl;
vis[x][i] = 1;
ull to = gethash(a[x],i,b[cnt2 - n + 2]);
b[++cnt2] = i;
int &d = num[to];
if ( !d ) d = ++cnt , a[cnt] = to;
deg[x]++ , deg[d]--;
dfs(d);
--cnt2;
if ( flag ){
ans2[++tot2] = i;
}
else{
ans[++tot] = i;
}
if ( flag && deg[x] == 1 ){
id = tot;
rep(i,1,n - 1) st[i] = b[cnt2 + i - (n - 1)];
// cout<<"st : \n";
// rep(i,1,n - 1) cout<<st[i] + 1;
// cout<<endl;
flag = 0;
}
if ( k == -1 ) return;
}
}
int main(){
// freopen("input.txt","r",stdin);
scanf("%d %d %d",&n,&m,&k);
rep(i,1,m){
scanf("%d",&c[i]);
z[i - 1] = c[i];
}
if ( n == 1 ){
rep(i,1,k) printf("%d",z[i - 1]);
puts("");
return 0;
}
/* if ( n > 40 ){
srand(20000907);
rep(i,1,k + n - 1) printf("%d",z[rand() % m]);
puts("");
return 0;
}*/
pow_[0] = 1;
rep(i,1,n) pow_[i] = pow_[i - 1] * base % mod;
rep(i,1,n - 1) b[++cnt2] = 0;
a[++cnt] = 0 , num[0] = 1 , dfs(1);
// rep(i,1,tot2) cout<<ans2[i] + 1;
// cout<<endl;
if ( !id ){
rep(i,1,tot2) ans[++tot] = ans2[i];
rep(i,1,n - 1) ans[++tot] = 0;
repd(i,tot,1) printf("%d",z[ans[i]]);
}
else{
rep(i,id + 1,tot) ans2[++tot2] = ans[i];
rep(i,1,id) ans2[++tot2] = ans[i];
rep(i,1,n - 1) ans2[++tot2] = st[n - i];
repd(i,tot2,1) printf("%d",z[ans2[i]]);
}
puts("");
// cout<<n<<endl;
}