CF#321-DIV2-E. Kefa and Watch-线段树+字符串哈希

本文介绍了一种结合线段树和字符串Hash解决区间修改及查询循环节的问题。通过维护字符串的Hash值,在线段树中高效实现区间更新与查询。适用于处理大规模字符串操作。

http://codeforces.com/contest/580/problem/E

题意

给你一段长度为n的字符串,给你m,k,表示有m+k次操作

格式 operation L R D

operation =1; 表示把第L到第R个字符改为d

operation =2;表示求 L到R这个字符串是否存在长度为d的循环,是输出YES,否则NO

n=10^5;  操作也是10^5

思路:

一开始看到区间修改,觉得可以是线段树...

然后要  查询循环节。。。想到kmp..O(N),也不行,必须得logn

但是还是用到了MP算法的一个应用,求循环节

http://blog.youkuaiyun.com/viphong/article/details/48498595  //此题是求循环节个数

由其中结论可知,  对于 一个长度为N的字符串,如果字符串【1,n-k】与【k,n】是相等的,那么说明k是该字符串的循环节长度。

从而,对操作二,查询【L,R】是否有d长度的循环节,只需要查询 【L,R-d】和【d,R】是否相等即可。此处相等显然不能用一个个字符去匹配(超时),因此我们可以用字符串hash的方法, 如果两个字符串经过hash函数计算得到的 hash值一样,我们就可以很大概率确定他们是同一个字符串了  (为提高正确率,取了2次hash,AC之后只取一个hash也能ac)

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

关于字符串hash的 解释: 

哈希本质上是一种映射 好的hash需要避免冲突

而字符串哈希,就是希望把一个字符串经过hash函数计算后,转化成一个数字。

最好的结果,就是希望任一个字符串,经过hash函数计算后,都转化成一个不同的数字(不可能完全做到,但是好的hash函数可以很大概率做到)

本题我们需要判定2个字符串是否相等,那么只需要分别转成2个数字,比较是否相等,相等则近似认为字符串相等,反之认为不等

因此hash函数需要满足

1【不同字符串得到的哈希值尽可能不等】

2【相等字符串得到的哈希值一定相等】


例如字符串abc 。我们选择7作为基底

得到一个ans= a*7^2+b*7^1+c*7^0 这个ans就是abc字符串得到的哈希值,

对应的hash函数为:

int hash(string i)
{
 int l=i.length, s=7,ans=0,t=1e9+7;
 for (int q=0;q<l;q++)
   {
     ans+=i[q]*s;
     s*=7;
     ans=ans%t;
   }
return ans;
此处取mod为1000000007;

只要字符串一样,经过hash得到的ans必然一样,如果字符串不一样,得到的hash是不一样的(理想的情况下,当然本处选7为基底是很大几率出现冲突的)

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

知道怎么计算字符串的哈希值后,显然我们不能每次对字符串求一次hash,我们需要在线段树里维护字符串的hash值

用得到的数有

int    add[4*N] ;//延迟标记
__int64 prime[4*N]; //存hash基底的i次方
__int64 sum[4*N]; ///存hash基底的前i次方和(即系数全为1)(为操作1服务)
__int64 hash[4*N];  //存当前节点管辖字符串的 hash值
__int64 mod;
__int64 fact; //hash的基底 一个大质数 

先预处理好质数fact的i次方,存到prime[]

然后sum存的是  hash基底的前i次方和 //每一项系数为1

例如sum[7]= fact^7+fact^6+fact^5+fact^4+fact^3+fact^2+fact^1;

***************

好,接下来我们讲怎么维护字符串的hash值:

*********************************************

【从两段儿子区间的hash值求到一段父区间的hash值】:

一开始递归建树的时候,最底层 l==r这种长度为1的区间时,显然是单个字符,其hash值为自身字符acsii码

现在假设先得到区间【1,1】的hash值为hash[1]、再得到【2,2】的hash值为hash[2]

那么我们要求【1,2】的hash值的时候,  

区间【1,1】记为A,区间【2,2】记为B,区间【1,2】记为C

需要由左子区间与右子区间的字符串合并计算......以左边为最低位的话 

hash[C]= hash[A]+ hash[B]* prime[len(A)]   //  len(A)是A字符串的长度,prime【len】是该长度对应的hash基底的i次方

那么现在我们就得到 从两段儿子区间的hash值求到一段父区间的hash值了

**********************************************

set操作改变一段字符串的hash值】:

假如我们要set一段区间【L,R】的值为 d

除了做延迟标记,我们利用之前的sum[]、直接维护该区间hash值

因为sum[]存的是 系数为1的 hash基底的前i次方和

那么,如果这段区间被set为d,显然 该段字符串的hash值为 sum[len-1]*d   //len为区间长度

用上面的维护方法维护涉及到的区间就好了 

**********************************************

基本用到的操作就是上面两个了,剩下的就是一些细节问题了。。。

例如 对字符串计算hash时,要用字符串的ascii码来计算,不要用阿拉伯数字来计算

因为001和01如果用阿拉伯数计算是用到0 0 1和 0 1 ,其hash值会一样。。。而用ascii就不会出现这样的情况

细节看代码吧。。。233


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
int n,m,k;
const int N = 100005  ;
int tm[N];
__int64 min(__int64 a,__int64 b)
{return a<b?a:b;}
__int64 max(__int64 a,__int64 b)
{return a>b?a:b;}
class tree
{
public:
	
	int    add[4*N] ;	//延迟标记
	__int64 prime[4*N]; //存hash数的i次方
	__int64 sum[4*N]; ///存hash数的前i次方和(即系数全为1)(为操作1服务)
	__int64 hash[4*N];  //存当前节点管辖字符串的 hash值
	__int64 mod;
	__int64 fact;		//hash数-质数 
	void build(int l,int r,int i)    //  线段树的建立;  
	{  
		add[i]=0;    
		if(l==r) 
		{
			hash[i]=tm[r];
			return ;
		}
		
		int mid=(l+r)>>1;  
		build(l,mid,i<<1);  
		build(mid+1,r,i<<1|1);   
		hash[i]=(hash[i<<1]+prime[mid-l+1]*hash[i<<1|1])%mod;
	}  
	
	void pushDown(int i, int l, int r)		//把i节点的延迟标记传递到左右儿子节点
	{
		if(add[i] != 0)
		{
			int mid = (l + r) >> 1;
			add[i << 1] = add[i];
			add[i << 1 | 1] = add[i];
			
			hash[i << 1] = (sum[(mid-l+1)-1] * add[i])%mod;  
			hash[i << 1 | 1]=(sum[ r-(mid+1)+1 -1 ] * add[i])%mod;   
			add[i] = 0;
		}
	}
	
	void update(int i, int l, int r, int ql, int qr, __int64 val) //更新区间为qlqr,当前区间为l,r,代表当前区间和的节点为i,更新值为val,
	{
		if(l > qr || ql > r)		//更新区间不在当前区间内
			return ;
		if(l >= ql && r <= qr)	//要更新的区间把当前区间完全包括,则把当前整个区间+val,然后返回上一层
		{	 
			add[i] = val;
			hash[i]=(val*sum[r-l+1-1])%mod;
			return ;
		}
		pushDown(i, l, r);			
		int mid = (l + r) >> 1;
		update(i << 1, l, mid, ql, qr, val);
		update(i << 1 | 1, mid + 1, r, ql, qr, val);
		hash[i]=(hash[i<<1]+prime[mid-l+1]*hash[i<<1|1])%mod;
	}
	
	__int64 query(int i, int l, int r, int ql, int qr)	 //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i 
	{
		if(l > qr || ql > r)
			return 0;
		if(l >= ql && r <= qr)
			return hash[i];
		pushDown(i, l, r);		
		int mid =( l + r) >> 1;
		__int64 t1= query(i << 1, l, mid, ql, qr) ;
		__int64 t2= query(i << 1 | 1, mid + 1, r, ql, qr);
		
		if (!t1) return t2%mod;//如果t1是return 0的情况,那么t2就不必乘上prime[len]了
		if (!t2) return t1%mod; 
		//return (t1+t2*prime[mid-l+1])%mod; 由于t1可能是属于return 0的情况,所以需要取max(l,ql),意义是取t1的有效长度
	
		return (t1+t2*prime[mid-max(l,ql)+1])%mod;
	} 
	void init()
	{ 
	//	memset(add,0,sizeof(add)); 
		prime[0]=1;
		sum[0]=1;
		int i;
		for (i=1;i<=N;i++)
		{
			prime[i]=(prime[i-1]*fact)%mod;
			sum[i]=(sum[i-1]+prime[i])%mod;
		}
	}
};

tree tp1;
tree tp2;

int main()
{
	
	tp1.mod=1e9+7;
	tp2.mod=1e9+9;
	tp1.fact=799817;
	tp2.fact=451309;  
	tp1.init();
	tp2.init();

	int i,op,l,r,d;
	
    scanf("%d%d%d", &n,&m,&k);
	 
		for (i=1;i<=n;i++) 
		{
			scanf("%1d",&tm[i]);  
		 	tm[i]+='0';
		}
	tp1.build(1,n,1);
	tp2.build(1,n,1);

	for (i=1;i<=m+k;i++)
	{
		scanf("%d %d %d %d",&op,&l,&r,&d);
		if (op==1)
		{
			tp1.update(1,1,n,l,r,d+'0');
			tp2.update(1,1,n,l,r,d+'0');				 
		}
		else
		{
			if (r-l+1==d)
			{
				printf("YES\n");
				continue;
			}
			else
			{
				
				int l1=l;
				int r1=r-d;
				int l2=l+d;
				int r2=r; 
				__int64 t1=tp1.query(1,1,n,l1,r1);
				__int64 t2=tp1.query(1,1,n,l2,r2);
				__int64 t3=tp2.query(1,1,n,l1,r1);
				__int64 t4=tp2.query(1,1,n,l2,r2);
				if (t1!=t2 || t3!=t4)//任一hash函数的值不匹配
				{
					printf("NO\n");
				}
				else
				{
					printf("YES\n");
				} 
			}
		}	
	}
	
	return 0;
}







我现在要使用stata进行实证分析,实证模型如下gtfp1it=α+βkefa+γXit+μi+λt*ρp+ϵi,gtfp1为被解释变量,kefa为核心解释变量,政策冲击年份policy_year分别为2011年和2017年,代表第t年县级行政区i是否被纳入重点生态功能区,x为控制变量集,ϵ为随机干扰项;i表示市级行政区,t表示时间,p表示省级行政区,为提高因果推断的可靠性,本文控制了市级行政区固定效应id和省份—年份联合固定效应provinceid#year,将标准误聚类至市级行政区—年份层面,id_year,控制变量包括,1. 经济发展水平ed 2.产业结构is 3.创新能力creat 4.环境规制强度er 5.政府干预程度gover地6.财政压力pre 7.植被覆盖率plant 8.二氧化碳排放量co2,9,y ,现在我需要构建多时点双重差分模型,要求1.进行描述性统计并输出表格,2进行基准回归并输出表格,表格需要标注是否控制个体固定效应id和省份—年份联合固定效应provinceid#year 3进行创新cr中介机制分析并输出表格,进行环境规制er机制分析并输出表格 4,以2011和2017作为政策冲击时点进行多期did平行趋势检验,要求在进行均值计算后再进行平行趋势检验并输出表格 5,进行地区zone异质性分析,地区包括东中西,输出表格 6,进行安慰剂检验,要求根据kefa相关系数0.76输出结果图, 请确保所有命令完全契合我的模型和我的需求,要求命令必须真实可执行,需要调整的地方请做标注
最新发布
04-20
<think>嗯,用户的问题是关于如何在Stata中实现多时点双重差分模型(DID),包括多个步骤,比如描述性统计、基准回归、中介分析等等。首先,我需要回忆一下多时点DID和传统DID的区别。传统DID通常处理的是政策在某个时点统一实施的情况,而多时点DID则允许不同个体在不同时间点受到政策冲击。这种情况下,Stata的处理方法可能会有所不同,比如使用`reghdfe`或者`xtreg`等命令,并正确设置时间和个体固定效应。 然后,用户提到的具体步骤有六个部分,每个部分都需要对应的Stata命令。首先,描述性统计并输出表格。这里可能需要用到`summarize`或者`tabstat`,然后使用`estpost`和`esttab`来输出表格到Excel或者LaTeX格式。需要注意变量的选择是否符合模型需求,比如是否包括处理组和对照组的关键变量。 接下来是基准回归,需要标注固定效应控制情况。多时点DID通常需要控制个体和时间固定效应,可能还要加入其他控制变量。Stata中常用的命令是`reghdfe`,因为它可以高效处理高维固定效应。回归模型中的处理变量通常是交互项,比如`policy`变量(处理组虚拟变量)与时间虚拟变量的交互,或者直接生成处理时点后的虚拟变量。需要注意的是,用户提到的是多时点,所以可能需要用`c.政策时点变量##i.处理组变量`来生成交互项,或者用`eventstudy`等方法来处理。 第三个部分是中介机制分析,涉及创新cr和环境规制er。这里可能需要用到中介效应检验的方法,比如逐步法:首先回归政策对结果的影响,然后政策对中介变量的影响,最后结果和中介变量一起回归。使用`sgmediation`命令或者手动进行这三步回归。需要确保中介变量在理论上的合理性,并且控制相同的固定效应和变量。 第四是多期DID平行趋势检验,基于2011和2017政策冲击年份。平行趋势检验通常通过事件研究法,生成政策前后的时间虚拟变量与处理组的交互项,然后检验政策前的系数是否不显著。可以使用`eventstudyinteract`或者手动生成这些交互项,然后绘制系数图。注意政策年份可能不同,需要正确生成相对时间变量,比如`rel_year`,并排除政策实施当年的情况。 第五是地区异质性分析,分为东中西地区。这里可能需要分样本回归,或者生成地区与处理变量的交互项。使用`if`条件语句来分地区运行回归,或者用`c.处理变量##i.地区变量`来检验异质性。需要注意样本量是否足够,以及交互项的解释是否合理。 最后是安慰剂检验,基于kefa相关系数0.76。安慰剂检验通常包括随机分配处理组或随机政策时间,然后重复多次回归,观察估计系数的分布是否集中在零附近。用户提到的kefa相关系数可能是指某种特定的随机化方法,比如保持处理组的比例和实际相同,或者保持处理组之间的相关性结构。可能需要使用自举法(bootstrap)或者置换检验(permutation test),生成伪处理变量,然后运行基准回归,比较实际系数与安慰剂分布的差异。 在引用部分,用户提供的三个引用中提到了共同趋势假设、Time-varying DID的模拟以及PSM-DID的实现。需要确保在回答中提到的检验方法,如平行趋势检验和安慰剂检验,符合这些引用中的建议。例如,引用[1]提到共同趋势假设的重要性,所以在平行趋势检验部分需要强调这一点;引用[2]讨论了多时点DID的模型设置,可能影响基准回归的命令选择;引用[3]提到自助法用于稳健性检验,可能与安慰剂检验相关,但用户这里要求的是基于kefa相关系数的安慰剂检验,可能需要具体解释如何操作。 此外,需要注意用户要求所有命令真实可执行,所以需要检查命令的语法是否正确,比如`reghdfe`需要先安装,`esttab`属于estout包,`eventstudyinteract`可能需要安装外部命令。在回答中应提示用户安装必要的Stata包,并确保变量名称和数据结构符合命令要求。例如,处理组变量应为二元虚拟变量,时间变量应为连续或分类变量,地区变量需要正确分类为东中西。 最后,生成相关问题需要考虑用户可能的后续疑问,比如如何解释回归结果、处理异方差和自相关、其他稳健性检验方法等。需要确保相关问题覆盖用户可能深入的各个方面,帮助其进一步理解和应用多时点DID模型。</think>以下是多时点双重差分模型(Time-varying DID)在Stata中的实现方案,结合引用文献中的理论框架和实际操作需求: --- ### 一、描述性统计与输出 ```stata * 生成处理组虚拟变量 gen treated = (政策实施年份 != .) // 根据实际变量名称修改 * 输出描述性统计表 estpost summarize y cr er 控制变量1 控制变量2 treated esttab using "描述性统计.rtf", cells("mean(fmt(3)) sd(fmt(3)) min max") replace ``` --- ### 二、基准回归(控制双向固定效应) ```stata * 安装reghdfe包(如未安装) ssc install reghdfe * 生成多时点DID交互项 gen did = treated * 政策实施后年份虚拟变量 * 基准回归 reghdfe y did 控制变量1 控制变量2, absorb(个体id 年份) vce(cluster 地区id) est store m1 esttab m1 using "基准回归.rtf", se ar2 star(* 0.1 ** 0.05 *** 0.01) addnote("固定效应:个体+年份") replace ``` --- ### 三、中介机制分析(Bootstrap法) ```stata * 机制1:创新(cr) reghdfe cr did 控制变量1 控制变量2, absorb(个体id 年份) est store m_cr * 机制2:环境规制(er) reghdfe er did 控制变量1 控制变量2, absorb(个体id 年份) est store m_er * 合并输出 esttab m_cr m_er using "中介机制.rtf", se ar2 star(* 0.1 ** 0.05 *** 0.01) replace ``` --- ### 四、平行趋势检验(事件研究法) ```stata * 生成相对时间变量 gen rel_year = 年份 - 政策实施年份 // 根据2011和2017年分别生成 * 剔除政策实施当期(k=0) drop if rel_year == 0 * 动态效应检验 reghdfe y ibn.rel_year##treated 控制变量, absorb(个体id 年份) vce(cluster 地区id) coefplot, vertical keep(*treated) xline(0) yline(0) title("平行趋势检验") ``` --- ### 五、地区异质性分析 ```stata * 分样本回归 reghdfe y did 控制变量 if 地区分类==1, absorb(个体id 年份) // 东部 reghdfe y did 控制变量 if 地区分类==2, absorb(个体id 年份) // 中部 reghdfe y did 控制变量 if 地区分类==3, absorb(个体id 年份) // 西部 ``` --- ### 六、安慰剂检验(Kefa相关系数法) ```stata * 生成伪处理组(保持处理组比例和kefa相关系数0.76) preserve bootstrap r(tau), reps(500): psmatch2 treated 控制变量, outcome(y) kernel ties common restore * 绘制安慰剂检验分布图 parmest, label saving(placebo.dta, replace) twoway (histogram _b_did, fraction) (scatteri 0 `=real(_b[did])' 0.5, msymbol(X)), legend(off) ``` --- ### 理论要点说明 1. **共同趋势假设**需通过平行趋势检验验证,动态效应系数在政策前应不显著[^1] 2. **时间异质性处理**需注意政策效果的动态变化特征[^2] 3. **稳健性检验**应包含自抽样和伪处理组两种方法[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值