Wannafly挑战赛22游记
Wannafly挑战赛22游记
幸运的人都是相似的,不幸的人各有各的不幸。
——题记
A-计数器
题目大意:
有一个计数器,计数器的初始值为\(0\),每次操作你可以把计数器的值加上\(a_1,a_2,\ldots,a_n\)中的任意一个整数,操作次数不限(可以为\(0\)次),问计数器的值对\(m\)取模后有几种可能。
思路:
由裴蜀定理易得,答案即为\(\frac m{\gcd(m,a_1,a_2,\ldots,a_n)}\)。
源代码:
#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=101;
int a[N];
int main() {
const int n=getint(),m=getint();
int gcd=m;
for(register int i=1;i<=n;i++) {
a[i]=getint()%m;
gcd=std::__gcd(gcd,a[i]);
}
printf("%d\n",m/gcd);
return 0;
}
B-字符路径
题目大意:
给一个含\(n\)个点\(m\)条边的有向无环图(允许重边,点用\(1\)到\(n\)的整数表示),每条边上有一个字符,问图上有几条路径满足路径上经过的边上的字符组成的的字符串去掉空格后以大写字母开头,句号'.'
结尾,中间都是小写字母,小写字母可以为\(0\)个。
思路:
拓扑序上DP,记录每个点可以对应多少个大写字母开头的字符串,若在前面加上空格有多少种方案,在后面加上空格有多少种方案(反图)。若当前边为'.'
则计算对答案的贡献。
源代码:
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
inline int getch() {
register char ch=getchar();
while(!isalpha(ch)&&ch!='_'&&ch!='.') ch=getchar();
return ch;
}
const int N=5e4+1;
struct Edge {
int to;
char w;
};
std::vector<Edge> e[N],e2[N];
inline void add_edge(const int &u,const int &v,const char &w) {
e[u].push_back((Edge){v,w});
}
inline void add_edge2(const int &u,const int &v,const char &w) {
e2[u].push_back((Edge){v,w});
}
int n,m,ind[N],outd[N];
unsigned upper[N],space[N],space2[N],ans;
std::queue<int> q;
void kahn2() {
for(register int i=1;i<=n;i++) {
if(outd[i]==0) q.push(i);
}
while(!q.empty()) {
const int &x=q.front();
for(auto &j:e2[x]) {
const int &y=j.to;
const char &w=j.w;
if(w=='_') space2[y]+=space2[x]+1;
if(!--outd[y]) q.push(y);
}
q.pop();
}
}
void kahn() {
for(register int i=1;i<=n;i++) {
if(ind[i]==0) q.push(i);
}
while(!q.empty()) {
const int &x=q.front();
for(auto &j:e[x]) {
const int &y=j.to;
const char &w=j.w;
if(isupper(w)) upper[y]+=space[x]+1;
if(islower(w)) upper[y]+=upper[x];
if(w=='_') {
space[y]+=space[x]+1;
upper[y]+=upper[x];
}
if(w=='.') ans+=upper[x]*(space2[y]+1);
if(!--ind[y]) q.push(y);
}
q.pop();
}
}
int main() {
n=getint(),m=getint();
for(register int i=0;i<m;i++) {
const int u=getint(),v=getint();
const char w=getch();
add_edge(u,v,w);
add_edge2(v,u,w);
ind[v]++;
outd[u]++;
}
kahn2();
kahn();
printf("%u\n",ans);
return 0;
}
D-整数序列
题目大意:
给出一个长度为\(n\)的整数序列\(a_1,a_2,\ldots,a_n\),进行\(m\)次操作,操作分为两类:
- 给出\(l,r,v\),将\(a_{l\sim r}\)分别加上\(v\);
- 给出\(l,r\),询问\(\sum_{i=l}^r\sin(a_i)\)。
思路:
根据三角恒等变换:
\[\begin{align*}
\sin(\alpha+\beta)=\sin\alpha\cdot\cos\beta+\cos\alpha\cdot\sin\beta\\
\cos(\alpha+\beta)=\cos\alpha\cdot\cos\beta-\sin\alpha\cdot\sin\beta
\end{align*}
\]
用线段树维护区间\(\sum\sin(a_i)\)和区间\(\sum\cos(a_i)\)即可。
考虑写成复数的形式,\(\cos\)作为实部,\(\sin\)作为虚部。使用std::complex
可以很方便的维护。
源代码:
#include<cmath>
#include<cstdio>
#include<cctype>
#include<complex>
#include<algorithm>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
typedef long long int64;
typedef std::complex<double> comp;
const int N=2e5+1;
class SegmentTree {
#define _left <<1
#define _right <<1|1
#define mid ((b+e)>>1)
private:
comp val[N<<2],tag[N<<2];
void push_up(const int &p) {
val[p]=val[p _left]+val[p _right];
}
void push_down(const int &p) {
if(tag[p]==comp(1,0)) return;
val[p _left]*=tag[p];
val[p _right]*=tag[p];
tag[p _left]*=tag[p];
tag[p _right]*=tag[p];
tag[p]=comp(1,0);
}
public:
void build(const int &p,const int &b,const int &e) {
if(b==e) {
const int x=getint();
val[p]=comp(cos(x),sin(x));
return;
}
tag[p]=comp(1,0);
build(p _left,b,mid);
build(p _right,mid+1,e);
push_up(p);
}
void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const comp &x) {
if(b==l&&e==r) {
tag[p]*=x;
val[p]*=x;
return;
}
push_down(p);
if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x);
if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x);
push_up(p);
}
double query(const int &p,const int &b,const int &e,const int &l,const int &r) {
if(b==l&&e==r) return val[p].imag();
push_down(p);
double ret=0;
if(l<=mid) ret+=query(p _left,b,mid,l,std::min(mid,r));
if(r>mid) ret+=query(p _right,mid+1,e,std::max(mid+1,l),r);
return ret;
}
#undef _left
#undef _right
#undef mid
};
SegmentTree t;
int main() {
const int n=getint();
t.build(1,1,n);
const int m=getint();
for(register int i=0;i<m;i++) {
const int opt=getint(),l=getint(),r=getint();
if(opt==1) {
const int x=getint();
t.modify(1,1,n,l,r,comp(cos(x),sin(x)));
}
if(opt==2) {
printf("%.1f\n",t.query(1,1,n,l,r));
}
}
return 0;
}