2018HDU多校训练1

题目链接:https://cn.vjudge.net/contest/298618

A - Maximum Multiple

题意:给定一个n,找n的约数x,y,z,满足x+y+z=n,求 x ∗ y ∗ z x*y*z xyz的最大值
题解:转化一下,n=ax=by=cz,那么 x = n / a x=n/a x=n/a y = n / b y=n/b y=n/b z = n / c z=n/c z=n/c
原式转化为已知 1 / a + 1 / b + 1 / c = 1 1/a+1/b+1/c=1 1/a+1/b+1/c=1,求 n 3 / ( a ∗ b ∗ c ) n^3/(a*b*c) n3/(abc)的最大值
由于满足 1 / a + 1 / b + 1 / c = 1 1/a+1/b+1/c=1 1/a+1/b+1/c=1的整数对只有{2,2,4} {3,3,3} {2,3,6}我们只需求这几种情况的值即可

#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn=100010;

int n;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		ll ans=-1;
		if(n%3==0){
			ans=max(ans,1LL*n*n*n/27);
		}
		if(n%4==0){
			ans=max(ans,1LL*n*n*n/32);
		}
		if(n%6==0){
			ans=max(ans,1LL*n*n*n/36);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

B - Balanced Sequence(贪心)

HDU - 6299
题解转载自:https://blog.csdn.net/ACTerminate/article/details/81171799
题意:
给你n个包含’(‘与’)’的字符串,可以将这些字符串任意排序,求所有排序中,子序列是正规括号序列的最大长度。

题解:
首先我们对所有的字符串找到通过stack找到所有的串内正规括号子序列,之后剩下的串只有三种可能:

  1. 只包含’(’
  2. 先是一串’)’然后再是一串’(’
  3. 只包含’)’
    然后,按照第一类,第二类,第三类的顺序放置串。对于第二类内的排序,首先按照’(‘个数贡献正负排序,’)’个数小于’(‘个数则贡献为正,贡献是正的则排前面。然后正贡献的串,我们按照’)’个数从小到大排。负贡献的串,我们按照’(‘个数从大到小排。对于排序完的串,我们从前往后模拟记录一下即可。
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=(int)1e5+10;
struct node
{
    int a,b;
}p[MAXN];
char str[MAXN];
stack<char>s;
vector<node>v1,v2,v3;
bool cmp(node x,node y){
    if(x.a+x.b>=0&&y.a+y.b>=0)return x.a>y.a;
    else if(x.a+x.b>=0)return 1;
    else if(y.a+y.b>=0)return 0;
    else return x.b>y.b;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        while(!s.empty())s.pop();
        v1.clear();v2.clear();v3.clear();
        int step=0,n,ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",str);
            for(int j=0;str[j]!='\0';j++)
            {
                if(str[j]=='(')s.push(str[j]);
                else 
                {
                    if(!s.empty()&&s.top()=='(')s.pop(),ans++;
                    else s.push(str[j]);
                }
            }
            int a=0,b=0;    
            while(!s.empty())
            {
                if(s.top()=='(')b++;
                else a--;
                s.pop();
            }
            p[i].a=a;p[i].b=b;
            if(a==0&&b)v1.push_back(p[i]);
            else if(a&&b==0)v3.push_back(p[i]);
            else v2.push_back(p[i]);
        }
        for(int i=0;i<v1.size();i++)
            step+=v1[i].b;
        sort(v2.begin(),v2.end(),cmp);
        for(int i=0;i<v2.size();i++)
        {
            if(step<0)step=0;
            ans+=min(-v2[i].a,step);
            if(step+v2[i].a<0)
            {
                step=v2[i].b;
                continue;
            }
            step+=v2[i].a+v2[i].b;
        }
        for(int i=0;i<v3.size();i++)
        {
            if(step<0)step=0;
            ans+=min(step,-v3[i].a);
            if(step+v3[i].a<0)
            {
                step=0;
                break;
            }
            step+=v3[i].a;
        }
        printf("%d\n",ans*2);
    }
    return 0;
}

C - Triangle Partition

给n*3个点,构成n个三角形,输出任意一种方案,sort后直接输出即可。

#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn=3010;

int n;
struct Point{
	int x,y;
	int id;
}p[maxn];
bool cmp(const Point &a,const Point &b)
{
	if(a.x!=b.x) return a.x<b.x;
	return a.y<b.y;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=0;i<n*3;i++){
			scanf("%d%d",&p[i].x,&p[i].y);
			p[i].id=i+1;
		}
		int m=3*n;
		sort(p,p+3*n,cmp);
		for(int i=0;i+2<m;i+=3){
			printf("%d %d %d\n",p[i].id,p[i+1].id,p[i+2].id);
		} 
	}
	return 0;
}

D - Distinct Values

题解转载自:https://www.cnblogs.com/qldabiaoge/p/9356318.html
题意:
在同一个区间不能有相同的数,满足所有区间条件的最小的一组
这题就用L,R 两个指针维护这个ans序列,类似于莫队的区间维护

#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn=100010;

struct node{
	int l,r;
}a[maxn];
int b[maxn];
bool cmp(const node& a,const node& b)
{
	if(a.l!=b.l) return a.l<b.l;
	return a.r<b.r;
}
int n,m;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;i++)
			scanf("%d%d",&a[i].l,&a[i].r);
		sort(a,a+m,cmp);
		set<int> st;
		for(int i=1;i<=n;i++){
			b[i]=1;st.insert(i);
		}
		//printf("%d %d-\n",a[0].l,a[0].r);
		int L=a[0].l,R=a[0].r;
		for(int i=L;i<=R;i++){
			b[i]=*(st.begin());
			st.erase(st.begin());
		}
		for(int i=1;i<m;i++){
			while(L<a[i].l){
				st.insert(b[L]);
				L++;
			}			
			while(R<a[i].r){
				R++;
				if(R>=a[i].l){
					b[R]=*(st.begin());
					st.erase(st.begin());
				}
			}
		}
		for(int i=1;i<=n;i++)
			printf("%d%c",b[i],i==n?'\n':' ');
	}
	return 0;
}

E - Maximum Weighted Matching

HDU - 6302
题解转载自:https://blog.csdn.net/oidoidoid/article/details/82895471
大概就是通过一种操作得到一个图,求这个图的最大匹配,和取得最大的可能种数

思路倒是很容易看懂,这个代码也太emmm了吧

做法:

1.按照操作逆着dp,还原回最后只有一条边时的情况

2.注意处理每次更新的步骤(刚开始完全没想到还有可能已经存在一条相同的边,更新也一开始想错了

#include
#include
#include 
#include
#include
#include
#define go(i,a,b) for (ll i=a;i<=b;i++)
#define ll long long
#define N 100005
#define MOD 1000000007
using namespace std;
typedef pair<ll,ll>pii;
 
struct edge{
	ll u,v;
	ll dp[2][2],sum[2][2];
	bool operator<(const edge &e) const {
		return  v<e.v;
	}
	//set中的元素如果是结构体必须重载该运算符
	//因为set中的元素是按照大小排列的,erase比较的也是这个位置
	//这里的const如果不加的话运算符重载即失败 
};
void updata(ll &w, ll &sum, ll w1, ll sum1){
	if (w1==-1) return;
	if (w1>w) w=w1,sum=sum1;
	else if (w1==w) sum=(sum+sum1)%MOD;
	//更新数据 
}
set<edge> e[N];
int main(){
	ll T,u,v,w,ca,n,m;
	map<pii,pii>::iterator it;
	set<edge>::iterator sit;
	scanf("%lld",&T);
	for (ll ca=1;ca<=T;ca++){
		scanf("%lld%lld",&n,&m);
		map<pii,pii>mp;//读入存在map中,重复边仅保留最优,并保存最优边的个数 
		go(i,1,m){
			scanf("%lld%lld%lld",&u,&v,&w);
			if (u>v) swap(u,v);
			pii p=make_pair(u,v);
			if (!mp.count(p)) mp[p]=make_pair(w,0);
			it=mp.find(p);
			if (it->second.first<w) it->second=make_pair(w,1);
			else if (it->second.first==w) it->second.second++;
		}
		go(i,1,n) e[i].clear();//把map中的边都存到set中,好读取 
		for (it=mp.begin();it!=mp.end();it++){
			edge ne; 
			ne.u=it->first.first;
			ne.v=it->first.second;
			memset(ne.dp,-1,sizeof(ne.dp));
			memset(ne.sum,-1,sizeof(ne.sum));
			ne.dp[0][0]=0;
			ne.sum[0][0]=1;
			ne.dp[1][1]=it->second.first;
			ne.sum[1][1]=it->second.second;
			e[ne.u].insert(ne);
			swap(ne.u,ne.v);
			e[ne.u].insert(ne);
		}
		if (n==2){//只有两个点直接输出 
			it=mp.begin();
			printf("%d %d\n",it->second.first,it->second.second);
			continue;
		}
		queue<ll>q;
		go(i,1,n){//将度为2的点进队列 
			if (e[i].size()==2) q.push(i);
		}
		go(i,1,n-2){
			ll u=q.front();
			q.pop();
			edge a=*e[u].begin(); //a.v->a.u==b.u->b.v
			e[u].erase(e[u].begin());
			edge b=*e[u].begin();
			e[u].erase(e[u].begin());//删去这个点的两个出边 
			edge ne;
			ne.v=u;
			e[a.v].erase(ne);//删除到达当前点的入边 
			e[b.v].erase(ne);
			ne.u=a.v; 
			ne.v=b.v; 
			ll dp[2][2],sum[2][2];
			memset(dp,-1,sizeof(dp));memset(sum,-1,sizeof(sum));
			//两条边合并进行dp 
			go(m1,0,1)for (ll m2=0;m1+m2<=1;m2++){
				go(l,0,1)go(r,0,1){
					if (a.dp[m1][l]==-1||b.dp[m2][r]==-1) continue;
					updata(dp[l][r],sum[l][r],a.dp[m1][l]+b.dp[m2][r], (a.sum[m1][l]*b.sum[m2][r])%MOD);
				}
			}
			sit=e[ne.u].find(ne); //查找map 看有没有已经合并成l到r的边 
			bool flag=(sit!=e[ne.u].end());
			if (flag){ //若有再进行dp 
				ll dpf[2][2],sumf[2][2];
				memset(dpf,-1,sizeof(dpf));
				memset(sumf,-1,sizeof(sumf));
				//什么水平,这是什么水平
				//开始感觉这里应该是memcpy(dpf,dp,sizeof(dp)); 后来发现更新是可以更新到的 
				go(l1,0,1) go(r1,0,1){
					for (ll l2=0;l2+l1<=1;l2++)for (ll r2=0;r2+r1<=1;r2++){
						if (dp[l1][r1]==-1||sit->dp[l2][r2]==-1) continue;
						updata(dpf[l1+l2][r1+r2],sumf[l1+l2][r1+r2],dp[l1][r1]+sit->dp[l2][r2],(sum[l1][r1]*sit->sum[l2][r2])%MOD); 
					}
				}
				memcpy(dp,dpf,sizeof(dpf));
				memcpy(sum,sumf,sizeof(sumf));
				//又是什么恶魔操作memcpy 
				e[ne.u].erase(ne);
				swap(ne.u,ne.v);
				e[ne.u].erase(ne);
				swap(ne.u,ne.v);
				//双向是相同的不用再删除一次
			}
			go(l,0,1) go(r,0,1){
				ne.dp[l][r]=dp[l][r],ne.sum[l][r]=sum[l][r];
			}
			e[ne.u].insert(ne);
			swap(ne.u,ne.v);
			go(l,0,1) go(r,0,1){
				ne.dp[l][r]=dp[r][l],ne.sum[l][r]=sum[r][l];
			}
			e[ne.u].insert(ne);
			if (flag&&e[ne.u].size()==2) q.push(ne.u); //若产生了度为2的点入栈 
			if (flag&&e[ne.v].size()==2) q.push(ne.v);
		}
		ll dp=-1;
		ll sum=-1;
		edge ne=*e[q.front()].begin();
		go(l,0,1) go(r,0,1){
			updata(dp,sum,ne.dp[l][r],ne.sum[l][r]);
		} 
		printf("%lld %lld\n",dp,sum);
	}
	return 0;
} 
 
/*
1
4 5
1 2 1
1 3 1
1 4 1
2 3 1
3 4 1
*/

F - Period Sequence

HDU - 6303
代码来源:https://cn.vjudge.net/status/#un=&OJId=HDU&probNum=6303&res=1&language=&onlyFollowee=false

#include
using namespace std;
#define ll long long
const ll mod=1e9+7;
ll inv6,inv2,n,s[2010];
ll quickpower(ll x,ll y)
{
	ll res=1;
	while(y){
		if(y&1)
			res=res*x%mod;
		x=x*x%mod;
		y>>=1; 
	}
	return res;
}
ll cal(int f,ll x)
{
	x%=mod;
	ll tmp=(x*(x+1)/2)%mod;
	if(f==1)
		return tmp;//
	if(f==2)
		return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
	return tmp*tmp%mod;
}
ll sum(int f,ll l,ll r)
{
	return (cal(f,r)-cal(f,l-1)+mod)%mod;
}
int main()
{
	inv2=quickpower(2,mod-2);
	inv6=quickpower(6,mod-2);
	int t;
	scanf("%d",&t);
	while(t--){
		ll L,R;
		scanf("%lld%lld%lld",&n,&L,&R);
		ll ans=0;
		for(int i=0;i<n;i++){
			scanf("%lld",&s[i]);
		}
		for(int i=0;i<n;i++){
			for(int j=i;j<n;j++){
				if(s[i]%n!=s[j]%n)
					continue;
				ll l,r;
				ll d=(s[j]-s[i])/n;
				if(s[i]<=s[j]){
					l=max((L+n-1-j)/n+d,(L+n-1-i)/n);
					r=min((R-i)/n,(R-j)/n+d);
				}else{
					l=(L+n-1-i)/n;
					r=(R-j)/n+d;
				}
				if(l>r)
					continue;
				ll a,b,c;
				a=s[i];
				if(d<=0){
					b=i-L+1;
					c=R-(j-d*n)+1;
				}else{
					b=(j-d*n)-L+1;
					c=R-i+1;
				}
				b=(b%mod+mod)%mod;
				c=(c%mod+mod)%mod;
				ll res=0;
				res=(res-1LL*n*n%mod*n%mod*sum(3,l,r)%mod+mod)%mod;
				res=(res+(c-a-b)%mod*n%mod*n%mod*sum(2,l,r)%mod+mod)%mod;
				res=(res+(b*c%mod+c*a%mod-a*b%mod)%mod*n%mod*sum(1,l,r)%mod+mod)%mod;//
				res=(res+a*b%mod*c%mod*((r-l+1)%mod)%mod+mod)%mod;
				if(i!=j)
					res=res*2%mod;
				
				ans=(ans+res)%mod;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

G - Chiaki Sequence Revisited(计数找规律)

HDU - 6304

题解转载自:https://blog.csdn.net/hao_zong_yin/article/details/81181539
题意:

n=1,2时,a[n] =1;

n>=3时,a[n] = a[n-a[n-1]] + a[n-1-a[n-2]];

现在给定一个n(1<=n<=1e18),求前a的前n项和,结果mod(1e9+7)

思路:

大体思路:找规律->求出a[n]->求出a的前n项和

详细思路:

打表打出a后发现序列是这样的 1 1 2 2 3 4 4 4 5 6 6 7 8 8 8 8 9 10 10 …

我们把每个数值的出现次数打出来,得到:

数值: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16…

出现次数:2 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5…

1的出现次数破坏了我们接下来要讲的规律,所以我们将a[1] = 1拿出来单独讨论,找剩余元素的规律

再次将每个数值的出现次数打出来,得到:

数值: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16…

出现次数:1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5…

这是后就发现规律了,前2 ^ i个元素其实就是把前2(i-1)个元素复制两次,然后把第2i个元素的值加1

这样我们就可以得到元素出现次数上的规律了,设cnt[i]为前2^i个元素出现次数之和,那么cnt[i] = 2 * cnt[i-1] + 1

我们知道任何一个数都可以表示成若干个2的幂次方之和,那么给定一个下标n,我们便可以通过cnt求出a[n],具体做法如下:

ll a = 0;
for (int i = 62; i >= 0; i--) {
    while (cnt[i] <= n) {
        n -= cnt[i];
        a += (1LL<<i);
    }
}

这里我们顺便求一下a[n]这个值在a序列中第一次出现的位置,原理同上,这个之后要用到

ll pos = 0;
for (int i = 62; i >= 0; i--) {
    while ((1LL<<i) <= a) {
        a -= (1LL<<i);
        pos += cnt[i];
    }
}
return pos + 1;//这里+1是因为规律是从第二项开始的

下面就可以求和了,再次观察每个数值的出现次数:

数值: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16…
出现次数:1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5…
我们发现1,3,5,7,9,11,13,15…出现了1次
2,6,10,14…出现了2次
4,12…出现了3次

打表打下去就会发现数值为奇数1的数都出现了1次,数值为奇数2的数都出现了两次,数值为奇数*3的数都出现了3次…

这就提醒我们枚举数值的出现次数i,i对应的数值序列实际上是一个等差数列,比如2对应的2,6,10,14…就是一个等差数列,这个等差数列的首项是2(i-1),公差为2i,那么我们只要知道等差数列的项数就可以求和了。

之前我们求得了a[n],那么我们便可以通过a[n]确定等差数列的项数。我们设等差数列的尾项为an,则有an<=a[n],这样我们便可以用等差数列的通项公式求出an,然后便可以知道有多少项了。

之后我们就可以对每个等差数列求和,然后乘上相应的出现次数i,理论上就可以得到结果了,但是还没完,因为等差数列中的尾项不一定出现i次,举个例子,n=10,那么可以求出a[10] = 6,出现2次的数值理论上有2,6,但是仔细看一下对于n=10,6这个数值是不是只出现了1次?这就提醒我们对于边界要特判,现在就可以考虑一下我之前为什么要求a[n]这个值在a序列中第一次出现的位置了,具体做法说起来比较乱,看代码吧,其实也不用非得像我这么做。

最后再次强调一下取模,能取模的地方尽量取!

到此就解决了这道题

#include 
#include 
#include 
#include 
#include 
 
using namespace std;
 
typedef long long ll;
const ll mod = 1e9 + 7;
 
ll cala(ll n) {//给定n,计算a[n]
    //因为规律是从第二项开始的,所以一开始先特判一下
    if (n == 1) return 1;
    n--;
    
    ll cnt[64];//cnt[i]表示数值1~2^i每项的出现次数之和
    cnt[0] = 1;
    for (int i = 1; i <= 62; i++) cnt[i] = 2*cnt[i-1] + 1;
    
    ll a = 0;
    for (int i = 62; i >= 0; i--) {
        while (cnt[i] <= n) {
            n -= cnt[i];
            a += (1LL<<i);
        }
    }
    return a;
}
 
ll posa(ll a) {//求a[n]这个值在a序列中第一次出现的位置
    //同样先特判一下
    if (a == 0) return 1;
    
    ll cnt[64];//cnt[i]表示数值1~2^i每项的出现次数之和
    cnt[0] = 1;
    for (int i = 1; i <= 62; i++) cnt[i] = 2*cnt[i-1] + 1;
    
    ll pos = 0;
    for (int i = 62; i >= 0; i--) {
        while ((1LL<<i) <= a) {
            a -= (1LL<<i);
            pos += cnt[i];
        }
    }
    return pos + 1;//这里+1是因为规律是从第二项开始的
}
 
int main() {
    int T;
    ll N;
    scanf("%d", &T);
    while (T--) {
        scanf("%lld", &N);
        ll a = cala(N);//求a[n]
        ll cnt = N - posa(a-1);//cnt为a[n]在序列中出现的次数
        ll ans = 0;
        for (int i = 1; (1LL<<(i-1)) <= a; i++) {
            //计算等差数列首项,公差,项数,尾项
            ll a1 = (1LL<<(i-1));
            ll d = (1LL<<i);
            ll n = ((a - a1) % d == 0 ? (a-a1)/d : (a-a1)/d+1);//注意一下这个判断
            ll an = a1 + d * (n-1);
            //求和,用了一下费马小定理
            ll sum = (((a1%mod + an%mod)%mod) * (n%mod)) % mod * 500000004 % mod;
            ans = (ans%mod + (sum%mod*i%mod)%mod) % mod;
            if ((a-a1) % d == 0) ans = (ans%mod + (cnt%mod*a%mod)%mod) % mod;//注意一下这个判断
        }
        printf("%lld\n", ans + 1);//规律是从第二项开始的,所以ans+1
    }
    return 0;
}

H - RMQ Similar Sequence(笛卡尔树)

HDU - 6305

题解转载自:https://blog.csdn.net/luyehao1/article/details/81280093
题意:

设RMQ(A,l,r)为最小的 i ,使得a[i]是a[l]-a[r]中的最大值。若A、B两个数组RMQ相似,则A、B等长,且在1<=l<=r<=n内,RMQ(A,l,r)=RMQ(B,l,r)。现在A数组已知,B数组在[0,1]均匀分布,设B数组的权重为数组内各元素的和。若A与B相似,求B权重的期望。
官方题解:

MQ-Similar实际上就是 A和 B的笛卡尔树一样,这样我们就有了一个二叉树,然后可以在树上分析了。考虑到B中有元素相同的概率是 0,于是可以假设 B里面元素互不相同,也就是说可以假定是一个排列。显然,符合笛卡尔树的排列就是这个树的拓扑序列个数,就是 n ! 2 ∏ s i z e [ i ] \frac{n!}{2\prod size[i]} 2size[i]n!。然后显然每个排列期望的和是 n 2 \frac{n}2 2n,于是答案就是 n 2 ∏ s i z e [ i ] \frac{n}{2\prod size[i]} 2size[i]n

笛卡尔树:

1.树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列(左儿子的key值小于自己,右儿子的key值大于自己)。

2.树中节点满足堆性质,节点的val值要大于其左右子节点的val值。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
 
typedef long long ll;
 
const int MAX = 1e6+10;
const ll mod = 1e9+7;
 
int l[MAX], r[MAX], vis[MAX], stk[MAX], inv[MAX], siz[MAX];
int n;
ll a[MAX];
 
 //建笛卡尔树模板,返回的是树的根节点下标
int build() {
    int top = 0;
    for(int i=1;i<=n;i++) l[i] = r[i] = vis[i] = 0;
    for(int i=1;i<=n;i++){
        int k = top;
        while(k > 0 && a[stk[k - 1]] < a[i]) --k;
        if(k) r[stk[k-1]] = i;
        if(k<top) l[i] = stk[k];
        stk[k++] = i;
        top = k;
    }
    for(int i=1;i<=n;i++) vis[l[i]] = vis[r[i]] = 1;
    int ret = 0;
    for(int i=1;i<=n;i++) if(vis[i] == 0) ret = i;
    return ret;
}
 
void dfs(int root)
{
    siz[root]=1;
    if(l[root]){
        dfs(l[root]);
        siz[root]+=siz[l[root]];
    }
    if(r[root]){
        dfs(r[root]);
        siz[root]+=siz[r[root]];
    }
}
 
int main()
{
    int T;
    scanf("%d",&T);
    //求逆元模板
    inv[1]=1;
    for(int i=2;i<MAX;i++)
        inv[i] = 1LL * inv[mod%i]*(mod-mod/i)%mod;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        int root=build();
        for(int i=0;i<=n;i++)
            siz[i]=0;
        dfs(root);
        ll ans=1;
        for(int i=1;i<=n;i++)
            ans=ans*inv[siz[i]]%mod;
        ans=(ans*n)%mod*inv[2]%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

J - Turn Off The Light

HDU - 6307
题解转载自:https://blog.csdn.net/qq_18869763/article/details/81193622
规律:如果从1开始往右走的话,如果从(1~i)经过的零的个数和有奇数个零的话(那么i与下个数必定要有一个来回),可以手动模拟样例。

#include 
using namespace std;
typedef long long LL;
 
const int N=1010000;
const int mod = 1e9+7;
int n,T;
bool f[N],g[N],preg[N],preg2[N];
char s[N];
int ret[N],ret2[N],cnt[N],cnt2[N];
void work(bool *g,int *ret,int n) {
	for(int i = 1; i <= n; i++) ret[i]=1<<30;
	int fo=n+1,lo=-1;
	for(int i = 1; i <= n; i++) {
		if (g[i]) {
			if (fo==n+1) fo=i;
			lo=i;
		}
	}
	if (fo>lo) {
		for(int i = 1; i <= n; i++) ret[i]=0;
		return;
	}
	for(int i = 1; i <= n; i++) {
	    //preg前缀0的个数是奇数(1)还是偶数(0)
        //preg2前缀1的个数是奇数(1)还是偶数(0)
        //cnt[i]为[1,i]中preg为奇数的位置的个数
        //cnt2[i]为[1,i]中preg2为奇数的位置的个数
		preg[i]=preg[i-1]^g[i]^1;
		preg2[i]=preg2[i-1]^g[i];
		cnt[i]=cnt[i-1]+preg[i];
		cnt2[i]=cnt2[i-1]+preg2[i];
	//	cout<
	}
	for(int i = 1; i <= lo; i++) {
		if (i<=fo) {
			int fw=i,lw=lo;
			if (i==lo) {
				ret[i]=min(ret[i],3);
				continue;
			}
			int ans=lw-fw;
			//前缀为0个数为奇数,出发点在fw,i的值固定为0,需要来回的次数。
			if (preg[fw-1]) ans+=2*(cnt[lw-1]-cnt[fw-1]);
			else ans+=2*(lw-fw-cnt[lw-1]+cnt[fw-1]);
			//区间总共偶数个零ans--,出发点在i,区间偶数个零停在lo-1处,最后一步不走。
			if (preg[lw-1]^preg[fw-1]^1) ans--;
			ret[i]=min(ret[i],ans);
		} else {
		    //[fw,i]为第一段,[i,lo]为第二段
		    //先走到最左,走过一遍后,被走过的位置1变成0,0变成1,所以用
			int fw=fo,lw=lo;
			int ans=i-fo+lw-fw;
			//到达最左后出发点为fw,fw的值固定为0,故同上操作。
			if (preg2[fw-1]) ans += 2*(cnt2[i-1]-cnt2[fw-1]);
			else ans+=2*(i-fw-cnt2[i-1]+cnt2[fw-1]);
 
            //到达此处时已经到达i处,且i以前全为0,但i自己的值不固定
			int x=preg2[i-1]^preg2[fw-1]^1;
			//前缀0的个数要与前一段区间的奇偶行相反,
			//x为1则代表从i的地方出发i的位置上为1等同于preg[i-1]为奇数的情况,都要在i-1与i之间来回一次(即ans+=2),
			//否则i的位置上为0等同于preg[i-1]为0的情况
			if (x==preg[i-1]) ans+=2*(cnt[lw-1]-cnt[i-1]);
			else ans+=2*(lw-i-cnt[lw-1]+cnt[i-1]);
			//若x=1,则第一段区间零的个数为偶数,则第二段区间零的个数为偶数,和为偶数;
			//若x=0,则第一段区间零的个数为奇数,则第二段区间零的个数为奇数,和为偶数。
			if (x^preg[i-1]^preg[lw-1]) ans--;
			ret[i]=min(ret[i],ans);
		}
	}
}
void solve() {
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i = 1; i <= n; i++) g[i]=(s[i]=='1');
	work(g,ret,n);
	reverse(g+1,g+n+1);
	work(g,ret2,n);
	LL ans=0;
	for(int i = 1; i <= n; i++) {
		ret[i]=min(ret[i],ret2[n+1-i]);
		ans=(ans+(LL)i*ret[i])%mod;
	}
	printf("%lld\n",ans);
}
int main() {
#ifdef LOCAL
    freopen("input.txt","r",stdin);
#endif // LOCAL
	for (scanf("%d",&T); T; T--) solve();
}

K - Time Zone

给北京时间,求其他时区的时间
用cin超时了,double类型精度不够,后来改成用int,把时间先全部转化为分钟。
可以用sscanf(s , “%lf”, &d);来把字符串转为double类型,很方便。对于这题,尝试了用double来做,发现(int)(c60)会wa,而用(int)(c10)*6就过了。。

#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn=3010;

int n;

int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		int a,b,sum=0;
		char s[10];
		scanf("%d%d",&a,&b);
		scanf("%s",s);
		sum=a*60+b;
		int f=s[3]=='+'?1:-1;
		int c=s[4]-'0';
        if(strlen(s)>5&&s[5]!='.') c=c*10+s[5]-'0';//
        c*=60;
		if(s[strlen(s)-2]=='.') c+=6*(s[strlen(s)-1]-'0');

		sum+=c*f-8*60;
		sum+=24*60;
		int h=sum%(24*60);
		printf("%02d:%02d\n",h/60,h%60);
		 
	}
	return 0;
}

你可能感兴趣的:(集训)