L题题解

讲一下我个人的理解吧,其实是比赛完了看人家通过代码才知道怎么写的。

这题要倒着来进行模拟,大概的基本原理:对于某一行开关,假设它被进行了多次更改,但是只要我知道它最后一次被执行了该行开灯的操作,那么这一行灯的状态就是开。 并且,我是倒着模拟的,那么前面的操作就不能对后面的操作产生影响。 倒着操作的过程中,每次操作完一行,我就打标记,表示这行被操作过了,在倒着循环的过程中再遇到就可以跳过. 要注意的是,不管开灯还是关灯,操作完这一行的状态就定死了,也就是说每次操作完都要更新行和列,就是n和m表示剩下的可以被操作的行和列。


using namespace std;

#define ll long long

const ll N=1e6+5;

struct node
{
	ll x,y,z;
}; 

// x记录行/列, y记录第几行/第几列, z记录开灯/关灯

node a[N];

ll f1[N],f2[N]; 

 //f1用来标记行有没有被操作过,f2用来标记列

int main()

{

    ll n,m,q,sum,i;
    cin>>n>>m>>q;
    ll ans=0;
    for(i=1;i<=q;i++)                      //因为要倒着模拟,所以先存下来
    {
        string s1,s2;
        ll p;
        cin>>s1>>p>>s2;
        
        if(s1!="row")	a[i].x=1;        //a[i].x为0表示是行操作,为1表示为列操作。 
        
        a[i].y=p;            //a[i].y存第几行或者第几列。 

        if(s2=="on")
        {
            a[i].z=1;       //a[i].z为1表示是开,为0表示关。 
        }    
    }
    
    for(i=q;i>=1;i--)
    {
    	if(a[i].x==0) 
    	{
    		if(f1[a[i].y]) continue;   //被标记就跳过
            
    		f1[a[i].y]=1; 			   //防止重复加 
            
    		if(a[i].z)   ans+=m;   
    		n--;          //可开关行减一 (前面的操作会被后面覆盖,所以后面操作过了就不用管前面怎么变了)
		}
        else 
        {
            if(f2[a[i].y]) continue;
            
            f2[a[i].y]=1;
            
            if(a[i].z)	ans+=n;
            
            m--;         //可开关列减一 
         }
	}
	cout<<ans;
	return 0;
    
}