2017.9.20
小 Z 学数学(math)
要说小 Z 最不擅长的学科,那一定就是数学了。这不,他最近正在学习加法运算。老师为了考核小 Z,给他出了一个问题。
给定一个操作序列,每个操作形如 t a 。如果 t 是 0,那么意味着加上 a;
如果 t 是 1,那么意味着改成 a。那么问题来了,给定一开始有一个数字 0,按照从左到右的顺序执行操作序列中[l,r]段的操作,最后得到的数字是什么?
为了确定小 Z 已经掌握了加法运算,老师给了他许多询问。小 Z 这下可慌了,因为他昨天晚上都在 dota,没有学习,自然也就回答不出问题。他准备向你求助,你能告诉他每个询问的答案吗?
由于数据过大,所以小 Z 告诉了你数据的生成方式,并且你只要告诉他所有答案按照指定方式加密后的结果就行了。
[输入格式]
从 math.in 中读取数据。
一行三个数字 n,m,seed 表示操作数量和询问数量,以及用于生成接下来所需要的数据的变量。
生成数据的模板在下发文件 math.cpp 中给出。
[输出格式]
假设 m 个询问按照输入的顺序分别是 a1,a2,…,am,你需要输出一个整数,它等于
∑ =1 233 ∗ 对 998244353 取膜的结果。
[样例输入]
5 5 23333
[样例输出]
50333483
[样例解释]
获得的数据分别是
0 880345150
1 787931255
0 2943464295
1 876592220
0 2063957658
3 3
1 4
2 5
2 4
3 5
询问的答案是 2943464295,876592220,2940549878, 876592220, 2940549878
输出 50333483
[数据范围与约定]
对于 30%的数据 n,m<=5000
对于 100%的数据 n,m<=10^7 0<=seed<=10^9
数据生成代码如下:
#include<iostream> #include<cstdio> using namespace std; unsigned int seed,a[10000005]; int n,m,l[10000005],r[10000005],ans=0; bool type[10000005]; unsigned int GetNext() { seed^=(seed<<7); seed^=(seed>>8); seed^=(seed<<13); return seed; } int main() { scanf("%d%d%u",&n,&m,&seed); for(int i=1;i<=n;++i) type[i]=GetNext()%2,a[i]=GetNext(); //获得n个操作的t和a for(int i=1;i<=m;++i) //获得m个询问的左右端点 { l[i]=GetNext()%n+1,r[i]=GetNext()%n+1; int tmp; if(l[i]>r[i]) tmp=l[i],l[i]=r[i],r[i]=tmp; long long res = 0; // blablabla... } cout<<ans; return 0; }
题解:显然赋值操作是会覆盖掉加操作的,所以只要求右端点之前的最后一个赋值操作,然后答案就是赋的值加上后面一段区间的和啦。复杂度 O(n) 。注意爆 longlong 。。。
#include<iostream> #include<cstdio> #define MN 10000005 using namespace std; const int mod=998244353; unsigned int seed,a[MN]; unsigned long long sum[MN],ans[MN]; int n,m,l[MN],r[MN],z[MN]; bool type[MN]; unsigned int GetNext(){ seed^=(seed<<7); seed^=(seed>>8); seed^=(seed<<13); return seed; } int main(){ freopen("math.in","r",stdin); freopen("math.out","w",stdout); scanf("%d%d%u",&n,&m,&seed); for(int i=1;i<=n;++i){ type[i]=GetNext()%2,a[i]=GetNext(); if(type[i]) z[i]=i;else z[i]=z[i-1]; sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=m;++i){ l[i]=GetNext()%n+1,r[i]=GetNext()%n+1; int tmp; if(l[i]>r[i])tmp=l[i],l[i]=r[i],r[i]=tmp; ans[i]=sum[r[i]]-sum[max(l[i],z[r[i]])-1]; } long long res=233,nn=0; for(int i=1;i<=m;i++,res=res*233%mod) nn=(nn+res*(ans[i]%mod)%mod)%mod; cout<<nn; }
小 Z 学英语(english)
好不容易混过了数学课,英语课又快开始了。小 Z 十分紧张,毕竟他还没有背好要考的单词,所以他想争取在英语课之前背完单词。小 Z 要背的单词有 n
个,并且他们有着相同的长度。为了节约时间,小 Z 准备将一些循环同构的单词一起背。
我们称两个单词循环同构,当且仅当满足至少一个下面的条件。
1) 两个单词相同
2) 把其中一个单词的最后一位接到最前面(例如”abc”变成”cab”),得到的单词和另一个单词循环同构。
例如”abc”和”abc”,”cab”,”bca”循环同构。
小 Z 现在想知道这些单词中有多少对<i,j>(i<j),满足第 i 个单词和第 j 个单词循环同构?
[输入格式]
从 english.in 中读取数据。
第一行两个数 n,m,表示有 n 个单词,每个单词的长度是 m 。
接下来 n 行,每行读入一个长度为 m 的单词,单词只包含小写字母。
[输出格式]
输出一个数字表示答案。
[样例输入]
4 4
abcd
acbd
bcda
dabc
[样例输出]
3
[样例解释]
abcd、bcda、dabc 循环同构,答案是 3
[数据范围与约定]
本题采用子任务制,只有你通过一个 subtask 内所有的数据点才能得到这个数据点对应的分数。
Subtask1 包含 30Points,满足 1<=n,m<=300
Subtask2 包含 30Points,满足 m<=5
Subtask3 包含 40Points 没有特殊限制条件
对于所有数据,满足 1<=n,m<=10^6 n*m<=10^6
题解:我们发现字符串循环同构虽然长的不一样,但是它的最小表示一定一样。所以我们可以每次 O(n)求一下最小表示,然后通过哈希计算答案。复杂度是O(nm+nlogn)。
#include<iostream> #include<cstdio> #define MN 1000000 using namespace std; char st[MN*2+5];long long ans=0; int n,m,val[MN+5],c[MN+5][26],cnt=1; int Solve(){ int i=1,j=2; while(i<=m&&j<=m){ int k=0; while(st[i+k]==st[j+k]&&k+1<m) ++k; if(st[i+k]>st[j+k]) i=i+k+1; else j=j+k+1; if(i==j) ++j; } return i<j?i:j; } int main(){ freopen("english.in","r",stdin); freopen("english.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ scanf("%s",st+1); for(int j=1;j<=m;++j) st[j+m]=st[j]; int pos=Solve(),x=1; for(int i=0;i<m;++i){ if(!c[x][st[pos+i]-'a']) c[x][st[pos+i]-'a']=++cnt; x=c[x][st[pos+i]-'a']; } ans+=val[x]++; } cout<<ans; }

(WPSpdf转Word要钱。。。)
题解:首先答案肯定是那 m 个数字中的一个。我们考虑从大到小考虑每个数字,合法就输出。确定要判断的数字之后,就只剩下了两种数字,分别大等于或者小于要判断的数字。这里认为大等于的数字是合法的。我们用 f[i]表示 i 的能量值大等于要判断的数字最少需要填入几个合法的数字,那么对于确定的叶子结点,如果他合法,f[i]=0,否则 f[i]=INF。对于不确定的,f[i]=1。然后就有了转移,f[i]就等同于它的三个儿子中比较小的两个 f 的和。权值越小,f[]越小,合法的数字越多,显然可以用二分解决这个问题。
#include<algorithm> #include<iostream> #include<cstdio> #define MN 1000000 using namespace std; int s[MN+5],n,m,c[MN+5][3],cnt=0,res=0,mark[MN+5],L[MN+5],mid; int Dp(int x){ if(c[x][0]==-1) return mark[x]?1:(s[x]>=mid?0:1e9); else{ int s1=Dp(c[x][0]),s2=Dp(c[x][1]),s3=Dp(c[x][2]); if(s1>s2) swap(s2,s1);if(s2>s3) swap(s2,s3);if(s1>s2) swap(s1,s2); return min(n+1,s1+s2); } } int main(){ freopen("chemistry.in","r",stdin); freopen("chemistry.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) if(scanf("%d",&c[i][0])==-1) scanf("%d",&s[i]); else scanf("%d%d",&c[i][1],&c[i][2]); for(int i=1;i<=m;++i){ int x;scanf("%d",&x); mark[x]=1; } for(int i=1;i<=n;++i) if(mark[i]) L[++cnt]=s[i]; sort(L+1,L+cnt+1);int l=1,r=1e9; while(l<=r){ mid=l+r>>1; if(Dp(1)<=cnt-(lower_bound(L+1,L+cnt+1,mid)-L)+1) res=mid,l=mid+1; else r=mid-1; } printf("%d\n",res); return 0; }