解释一下大致思路:赋值得到的值都是基于历史操作中赋值常量操作赋的值或者初始值修改(不动或取反得来的) 于是我存下了所有赋值常量(T,F,U)的历史,并将其余赋值操作都与历史联系,最后一定是一些历史支配一些节点,只要考虑那些 U 支配的以及初始值支配的就好了
代码中tim为保存节点最新历史的,ope是历史的赋值(T,F,U,空),book保存了每个历史对应的是哪个节点,rel保存了节点和支配它的历史的关系(取反,不取反),siz保存了历史支配的节点数量
主要是想确认下思想正确性,如果没想错就想求调一下,因为第四个样例过不去
代码附思路:
#include<bits/stdc++.h>
using namespace std;
/*
18:57 -----------------------------------------------------------------------------------
先理一下思路,我现在求出了所有的点之间的relation,他们指向了tim[i],这构成了一个森林,假使这个森林中的一棵树的根节点有值,显然他是不用求的
没值的话要想一下.... 对于一棵根节点为root的树,假如root是一个节点最新开的tim,即tim[book[root]]==root,那么显然这棵树节点全为U,当且仅当ope[root]==3或rel[book[root]]==1
对于root不是节点开的最新tim即root!=tim[book[root]]的情况,可以证明(其实也不用)指向的root一定是有值或者是初始值的,因为就只有开始的时候和赋值时新开了tim啊...
那么也可以继续愉快地讨论了,由于指向的是初始值,初始值一定等于末值,那么,ope[root]就等于ope[tim[book[root]]]
19:23-----------------------------------------------------------------------------------
*/
/*
19:50-----------------------------------------------------------------------------------
做了一下发现不对,赶紧再理一下思路
现在被卡掉了两个点,那两个点都有一个共性:没有任何一个赋值操作,那么,最后一个讨论就不能仅仅是那么简单。
对于每一个ope为空的root,我们作以下分类:
1.这个root是最新开的
那么rel[book[root]]完全可以拿来用,假如 rel==1 ,那么ope[root]=3;
否则完全可以设ope=1
2.这个root是初始值
考虑深搜,更新出自己的值,对于搜到自己的情况:rel=1则搜到的所有root支配的节点都纳入U
否则也可以设ope=1
没搜到自己的话,就按常规来
20:04 -------------------------------------------------------------------------------
20:12思考了一下可行性,可能还要开一个数组...
*/
const int N=1e5+10;
int rel[N],ope[N],tim[N],book[N],l,siz[N];//rel:1为反,ope:1T,2F,3U
bool flag[N];
int mp[N];//0:unsearch 1:reverse 2:search but not reverse
vector<int>root;
void print(int n,int l){
cout<<endl;
for(int i=1;i<=l;i++)printf("%3d",ope[i]);
cout<<endl;
for(int i=1;i<=l;i++)printf("%3d",book[i]);
cout<<endl;
for(int i=1;i<=l;i++)printf("%3d",siz[i]);
cout<<endl;
for(int i=1;i<=n;i++){
cout<<i<<": "<<tim[i]<<" "<<rel[i]<<" "<<'\n';
}
cout<<endl;
}
void dfs(int poi){
if(ope[tim[book[poi]]]){
ope[poi]=ope[tim[book[poi]]];
if(rel[book[poi]]&&ope[poi]!=3)ope[poi]=3-ope[poi];
// cout<<"-1\n";
}
else if(mp[tim[book[poi]]]){//搜过了,但是没有赋值,换言之就是这次搜索的前面的节点
int pos=tim[book[poi]],fl=rel[book[poi]];
if(((mp[poi]%2)^fl)==mp[pos]%2)ope[poi]=1;
else ope[poi]=3;
// cout<<"-2\n";
}
else{
int pos=tim[book[poi]],fl=rel[book[poi]];
mp[pos]=(((mp[poi]%2)^fl)?1:2);
dfs(pos);
ope[poi]=ope[pos];
if(rel[book[poi]]&&ope[poi]!=3)ope[poi]=3-ope[poi];
// cout<<" -3\n";
}
// cout<<poi<<" "<<mp[poi]<<"-<"<<endl;
}
void slove(){
root.clear();
memset(flag,0,sizeof flag);
memset(mp,0,sizeof mp);
memset(rel,0,sizeof rel);
memset(ope,0,sizeof ope);
l=0;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
tim[i]=++l;
book[l]=i;
siz[l]=1;
}
for(int i=1;i<=m;i++){
char v;
cin>>v;
if(v=='T'||v=='F'||v=='U'){
int id;
cin>>id;
siz[tim[id]]--;
tim[id]=++l;
siz[l]=1;
rel[id]=0;
book[l]=id;
if(v=='T')ope[l]=1;
if(v=='F')ope[l]=2;
if(v=='U')ope[l]=3;
}
else {
int id1,id2;
cin>>id1>>id2;
siz[tim[id1]]--;
siz[tim[id2]]++;
tim[id1]=tim[id2];
if(v=='+')rel[id1]=rel[id2];
else rel[id1]=1^rel[id2];
}
// print(n,l);
}
int ans=0;
// print(n,l);
for(int i=1;i<=n;i++){
if(!flag[tim[i]]){
flag[tim[i]]=1;
root.push_back(tim[i]);//方便调试的
}
}
for(int i:root){
if(ope[i]){
if(ope[i]==3)ans+=siz[i];
}
else if(i==tim[book[i]]){
if(rel[i])ans+=siz[i];
}
else {
dfs(i);
if(ope[i]==3)ans+=siz[i];
}
// cout<<i<<" "<<ans<<endl;
}
// print(n,l);
cout<<ans<<'\n';
}
int main(){
// freopen("tribool4.in","r",stdin);
cin.tie(0);
int c,T;//citykwl,rankokwl
cin>>c>>T;
while(T--)slove();
return 0;
}