noip12
T1
类似于昨天的t3,先用单调栈求出管控区间,然后暴力求解30pts
我没取模还没开longlong,然后就爆0了
正解:
- 仍然是用单调栈求一下区间,用线段树维护一下余数,对于i所对应的区间\([l_{i},r_{i}]\) ,当然是去枚举离i近的,剩下的用线段树求解,线段树要用动态开点,不然空间会炸。
Code
#include<cstdio>
#define K 1000010
#define MAX 300010
#define re register
#define int long long
namespace OMA
{
int n,k,ans;
int a[MAX],sum[MAX];
int root[K];
int top,L[MAX],R[MAX],sta[MAX];
struct Segmnet_Tree
{
int tot;
struct TREE
{
int res;
int ls,rs;
}st[K*50];
inline void Push_up(int p)
{ st[p].res = st[st[p].ls].res+st[st[p].rs].res; }
inline void insert(int &p,int l,int r,int pos)
{
p = (!p)?++tot:p;
if(l==r)
{ st[p].res = 1; return ; }
int mid = (l+r)>>1;
if(pos<=mid)
{ insert(st[p].ls,l,mid,pos); }
else
{ insert(st[p].rs,mid+1,r,pos); }
Push_up(p);
}
inline int query(int p,int l,int r,int lp,int rp)
{
if(l<=lp&&rp<=r)
{ return st[p].res; }
int sum = 0,mid = (lp+rp)>>1;
if(l<=mid)
{ sum += query(st[p].ls,l,r,lp,mid); }
if(r>mid)
{ sum += query(st[p].rs,l,r,mid+1,rp); }
return sum;
}
}Tree;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void STACK()
{
for(re int i=1; i<=n; i++)
{
while(top&&a[sta[top]]<=a[i])
{ R[sta[top--]] = i-1; }
L[i] = sta[top]+1;
sta[++top] = i;
}
while(top)
{ R[sta[top--]] = n; }
}
signed main()
{
n = read(),k = read();
for(re int i=1; i<=n; i++)
{ (sum[i] = sum[i-1]+(a[i] = read())%k) %= k; }
STACK();
for(re int i=1; i<=n; i++)
{ a[i] %= k; Tree.insert(root[sum[i]],1,n,i); }
for(re int i=1; i<=n; i++)
{
if(L[i]==R[i])
{ continue ; }
if(R[i]-i<i-L[i])
{
for(re int j=i+1; j<=R[i]; j++)
{
if(L[i]-1)
{ ans += Tree.query(root[(sum[j]-a[i]+k)%k],L[i]-1,i-1,1,n); }
else
{
int temp = (sum[j]-a[i]+k)%k;
ans += Tree.query(root[temp],L[i],i-1,1,n);
if(!temp)
{ ans++; }
}
}
if(L[i]-1)
{ ans += Tree.query(root[sum[i-1]],L[i]-1,i-2,1,n); }
else
{
if(i-2)
{ ans += Tree.query(root[sum[i-1]],1,i-2,1,n); }
if(!sum[i-1])
{ ans++; }
}
}
else
{
for(re int j=L[i]; j<=i-1; j++)
{ ans += Tree.query(root[(sum[j-1]+a[i])%k],i,R[i],1,n); }
if(i<R[i])
{ ans += Tree.query(root[sum[i]],i+1,R[i],1,n); }
}
}
printf("%lld\n",ans);
return 0;
}
}
signed main()
{ return OMA::main();}
T2
式子很好推,结果被我一顿乱搞+瞎取模取成了10pts。
首先正难则反(来自数学课),可以先求出变量互不相同时的式子
\[ans=\frac{A_{2^{n}}^{m}}{2^{n\times m}}=\frac{\begin{matrix} \prod_{i=2^{n}-m+1}^{2^{n}-1} \end{matrix}}{2^{n\times(m-1)}}
\]
然后我就码了个快速幂,求阶乘逆元gcd,一顿乱搞30pts->10pts
此时,我自己造了几个数据,发现显然不对,于是去想如何改一下。没想出来,交了个10pts。
正解:
推出式子后,考虑约分,发现只能约2及其倍数。所以去找有多少个2。
然后有个定理 \(2^{n}-a\)和\(a\) 这俩东西中,2的个数是相等的,所以找分子中2的个数,就转换成了去找 \((m-1)!\) 中2的个数,这个可以 \(O(logm)\) 求解。
对于\(m>mod\) 此时一定有一个数为mod的倍数,所以分子为0,但答案并非这个,因是%mod意义下的0,所以直接去计算分母就好了。
\(m\le mod\) 的话,直接暴力计算就好了。
Code
#include<cstdio>
#define MAX 1000100
#define re register
#define int long long
namespace OMA
{
int n,m,a,b;
int inv,c,cnt=63;
const int p = 1e6+3;
inline int quickpow(int a,int b)
{
int ans = 1;
while(b)
{
if(b&1)
{ ans = ans*a; }
a = a*a;
b >>= 1;
}
return ans;
}
signed main()
{
scanf("%lld%lld",&n,&m);
n %= p-1;
int tmp1 = 1,tmp2 = 0;
while(cnt--)
{ tmp1 <<= 1; if(tmp1>=m){ break; } (tmp2 += (m-1)/tmp1) %= p-1; }
c = quickpow(2,n),inv = quickpow(quickpow(2,tmp2),p-2);
b = quickpow(c,(m-1)%(p-1))*inv%p;
if(m<=p)
{
a = inv;
for(re int i=1; i<=m; i++)
{ (a *= c-i) %= p; }
a = ((b-a)%p+p)%p;
printf("%lld %lld\n",a,b);
}
else
{ printf("%lld %lld\n",b,b); }
return 0;
}
}
signed main()
{ return OMA::main(); }
T3
考试的时候写了个f**k大模拟+贪心,还过了自己造的hack数据,结果程序输出全是-1 10pts好成绩
考试做法错误是显然的,比如1 0 0 0 1,我的码会输出-1,因为是从前往后扫,出现次数超过1就换下一个值,顾前不顾后,wa也正常。
正解:
我们可以设二元组(a,b),表示当前位置上的数为a,其连续的长度为b。分别设两个 \(up\)和\(down\),up为能填的最大值,down为最小值,求up应该尽量往上,down相反。
最大值即为 \(up[n].a\),序列倒着就能求出来。具体实现见code。
Code
#include<cstdio>
#define MAX 200010
#define re register
namespace OMA
{
int n,m,cnt[MAX];
int a[MAX],ans[MAX];
struct pair
{ int a,b; };
pair up[MAX],down[MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline int min(int a,int b)
{ return a<b?a:b; }
signed LZk()
{
n = read();
for(re int i=1; i<=n; i++)
{ a[i] = read(); }
a[1] = 1;
up[1] = down[1] = (pair){1,1};
for(re int i=2; i<=n; i++)
{
up[i] = (pair){up[i-1].a,up[i-1].b+1};
down[i] = (pair){down[i-1].a,down[i-1].b+1};
if(up[i].b>2)
{ up[i] = (pair){up[i].a+1,1}; }
if(down[i].b>5)
{ down[i] = (pair){down[i].a+1,1}; }
if(a[i])
{
if(up[i].a>a[i])
{ up[i] = (pair){a[i],2}; }
if(down[i].a<a[i])
{ down[i] = (pair){a[i],1}; }
if(up[i].a<a[i]||a[i]<down[i].a)
{ printf("-1\n"); return 0; }
}
}
if(up[n].b==1)
{ up[n] = (pair){up[n-1].a,up[n-1].b+1}; }
printf("%d\n",up[n].a);
a[n] = up[n].a,cnt[up[n].a]++;
for(re int i=n-1; i>=1; i--)
{
if(!a[i])
{
int tmp = min(a[i+1],up[i].a);
if(cnt[tmp]==5)
{ tmp--; }
a[i] = tmp;
}
cnt[a[i]]++;
}
for(re int i=1; i<=n; i++)
{ printf("%d ",a[i]); }
return 0;
}
}
signed main()
{ return OMA::LZk(); }