题目链接:https://cn.vjudge.net/contest/298618
A - Maximum Multiple
题意:给定一个n,找n的约数x,y,z,满足x+y+z=n,求
x
∗
y
∗
z
x*y*z
x∗y∗z的最大值
题解:转化一下,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/(a∗b∗c)的最大值
由于满足
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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
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找到所有的串内正规括号子序列,之后剩下的串只有三种可能:
- 只包含’(’
- 先是一串’)’然后再是一串’(’
- 只包含’)’
然后,按照第一类,第二类,第三类的顺序放置串。对于第二类内的排序,首先按照’(‘个数贡献正负排序,’)’个数小于’(‘个数则贡献为正,贡献是正的则排前面。然后正贡献的串,我们按照’)’个数从小到大排。负贡献的串,我们按照’(‘个数从大到小排。对于排序完的串,我们从前往后模拟记录一下即可。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<stack>
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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
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<iostream>
#include<string.h>
#include<assert.h>
#include<set>
#include<map>
#include<queue>
#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<bits/stdc++.h>
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 <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
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]} 2∏size[i]n!。然后显然每个排列期望的和是 n 2 \frac{n}2 2n,于是答案就是 n 2 ∏ s i z e [ i ] \frac{n}{2\prod size[i]} 2∏size[i]n。
笛卡尔树:
1.树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列(左儿子的key值小于自己,右儿子的key值大于自己)。
2.树中节点满足堆性质,节点的val值要大于其左右子节点的val值。
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <algorithm>
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 <bits/stdc++.h>
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<<i<<" "<<cnt[i]<<endl;
}
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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
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;
}