A - Simple Math 2
推式子:
⌊
1
0
N
M
⌋
m
o
d
M
=
⌊
1
0
N
M
⌋
−
k
M
=
⌊
1
0
N
−
k
M
2
M
⌋
⌊{10^N\over M}⌋modM=⌊{10^N\over M}⌋-kM=⌊{10^N-kM^2\over M}⌋
⌊M10N⌋modM=⌊M10N⌋−kM=⌊M10N−kM2⌋
所以,我们快速幂取模求
1
0
n
m
o
d
m
2
10^n \mod m^2
10nmodm2后再整除M即可。
ll ksm(ll a,ll n,ll p){
ll res=1;
while(n){
if(n&1)res=res*a%p;
a=a*a%p;
n>>=1;
}
return (res%p+p)%p;
}
ll n,m;
int main(){
read(n),read(m);
printf("%lld\n",(ksm(10,n,m*m)/m+m)%m);
return 0;
}
B - Reversible Cards
和多校的题一样:https://ac.nowcoder.com/acm/problem/210022
对每一对a、b,我们可以看作有一条边连接着a、b,则输入就连为一个图,意义为:对于一个连通分支,每条边表示一次选择,每次可以选择这条边的一个端点。由此可得,对于一个连通分量,设若内部成环,则整个分量内的节点都可以选。否则,顶多选择总量-1个。据此并查集维护即可。
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);++i)
#define per(i,a,b) for(int (i)=(b);(i)>=(a);--i)
typedef long long ll;
template<typename T>
void read(T&x){
ll f=1;
x=0;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f*=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
//===========================================================
#define int ll
const int maxn=200000+100;
const int MAXN=400000+100;
int s[MAXN],du[MAXN];
int a[MAXN],b[MAXN];
int cnt[MAXN];
int n;
int find(int x){
return x==s[x]?x:s[x]=find(s[x]);
}
void merge(int x,int y){
x=find(x);
y=find(y);
if(x==y)return ;
s[x]=y;
}
set<int>ss;
set<int>S;
signed main(){
//freopen("in.txt","r",stdin);
rep(i,0,MAXN-1)s[i]=i;
read(n);
rep(i,1,n)read(a[i]),read(b[i]);
rep(i,1,n){
//cerr<<a[i]<<" "<<b[i]<<endl;
du[a[i]]++;
du[b[i]]++;
merge(a[i],b[i]);
S.insert(a[i]);
S.insert(b[i]);
}
for(auto i:S){
int fa=find(i);
ss.insert(fa);
cnt[fa]++;
if(i==fa)continue;
du[fa]+=du[i];
}
int ans=0;
for(auto fa:ss){
//cerr<<fa<<" "<<du[fa]<<" "<<cnt[fa]<<endl;
if(du[fa]/2==(cnt[fa]-1)){
ans+=cnt[fa]-1;
}
else{
ans+=cnt[fa];
}
}
printf("%lld\n",ans);
return 0;
}
C - Too Heavy
题意需要理解下。。
这也是一道通过图比较好找出思路的题。我们将1~n构建为一个有向图,构建方式为:
i->p[i]->p[p[i]]->……i。因为p为1~n的排列,所以一定存在回路。
对于其中一个连通分量,若其中存在一个点j,满足a[j]<=b[p[j]],也就是现在j身上的背包没法进行操作,则这个任务无法完成,直接-1。
接下来是如何构造的问题。我们找出这个连通分量中的一个点mxp,使得a[mxp]的这个连通分量中最大的,可得mxp这个人可以拿得起这个连通分量内的所有背包。则可以通过以下方式构造:
我们先要明确,对于这个回路中的任意一个点i,i身上的背包是要给i+1的,特殊的,n要给1。
对于i>mxp,因为mxp这个人可以拿所有背包,所以,我们以mxp为中介,mxp<一>i(建议手跑下)。
再对于i<mxp,mxp<一>i
经过这一过程,我们就可以完成交换。
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);++i)
#define per(i,a,b) for(int (i)=(b);(i)>=(a);--i)
typedef long long ll;
template<typename T>
void read(T&x){
ll f=1;
x=0;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f*=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+(ch-'0');
ch=getchar();
}
x*=f;
}
//=========================================
#define pii pair<int,int>
vector<pii>ans;
const int maxn=200000+100;
int a[maxn],b[maxn],p[maxn];
bitset<maxn>vis;
int n;
bool solve(){
rep(i,1,n){
if(!vis[i]){
vis[i]=1;
vector<int>cir;
int ptr=i;
cir.push_back(ptr);
ptr=p[i];
while(ptr!=i){
vis[ptr]=1;
cir.push_back(ptr);
ptr=p[ptr];
}
if(cir.size()==1)continue;
for(auto x:cir){
if(a[x]<=b[p[x]])return false;
}
int mxp=0;
for(int i=1;i<cir.size();i++){
if(a[cir[i]]>a[cir[mxp]])mxp=i;
}
for(int i=mxp+1;i<cir.size();++i){
ans.push_back(make_pair(cir[i],cir[mxp]));
}
for(int i=0;i<mxp;++i){
ans.push_back(make_pair(cir[i],cir[mxp]));
}
}
}
return true;
}
int main(){
//freopen("in.txt","r",stdin);
read(n);
rep(i,1,n)read(a[i]);
rep(i,1,n)read(b[i]);
rep(i,1,n)read(p[i]);
if(!solve())puts("-1");
else{
printf("%d\n",ans.size());
for(auto x:ans){
printf("%d %d\n",x.first,x.second);
}
}
return 0;
}
D - Orientation
题目保证数据一定有解,那么,我们其实就不用想太多了。对于有联通的i、j两点,我们只需要想到:
若c[i]>c[j],则i->j
若c[i]<c[j],则i<-j
那等于的时候呢?我们可以想到它应该是连成了一个环才会有这种情况发生,否则无解。那么,我们只需要沿着c和它相等、且相连的节点走个环即可。
注意,存在路径两端c相等不代表整个连通分量都需要在环上(我卡在了这里)。可以有独立的几个点是全部向外指出的,然后从环上走不到这几个点上。
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);++i)
#define per(i,a,b) for(int (i)=(b);(i)>=(a);--i)
typedef long long ll;
template<typename T>
void read(T&x){
x=0;
ll f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f*=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
//=====================================================
const int maxn=205;
int n,m;
struct Edge{
int to,next,id;
}e[maxn*maxn*2];
int head[maxn],cnt;
int mark[maxn][maxn];
int c[maxn];
int ans[maxn*maxn*2];
struct Da{
int u,v,num;
};
vector<Da>save;
void add(int x,int y,int z){
e[cnt].to=y;
e[cnt].next=head[x];
e[cnt].id=z;
e[cnt].next=head[x];
head[x]=cnt++;
}
bool vis[maxn];
void dfs(int u){
vis[u]=1;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(ans[e[i].id]==0){
ans[e[i].id]=(mark[u][v]?1:-1);
if(!vis[v])dfs(v);
}
}
}
int main(){
//freopen("in.txt","r",stdin);
memset(head,-1,sizeof(head));
read(n),read(m);
rep(i,1,m){
int x,y;
read(x),read(y);
save.push_back({x,y,i});
mark[x][y]=1;
}
rep(i,1,n)read(c[i]);
for(int i=0;i<save.size();i++){
auto x=save[i];
int u=x.u;
int v=x.v;
if(vis[u]&&vis[v])continue;
if(c[u]>c[v])ans[x.num]=1;
else if(c[u]<c[v])ans[x.num]=-1;
else{
add(u,v,i+1);
add(v,u,i+1);
}
}
rep(i,1,n){
if(!vis[i]){
dfs(i);
}
}
rep(i,1,m){
if(ans[i]==1)puts("->");
else if(ans[i]==-1)puts("<-");
else exit(-1);
}
return 0;
}
E - Simple Math 3
关于类欧几里得算法,我也不是很懂,贴板子吧~
洛谷模板题:
const int mod = 998244353;
#define i2 499122177ll
#define i6 166374059ll
struct node{
int f, g, h;
node() { f = g = h = 0; }
};
node cal(int n, int a, int b, int c){
int ac = a / c, bc = b / c, m = (a * n + b) / c, n1 = n + 1, n21 = n * 2 + 1;
node d;
if (a == 0){
d.f = bc * n1 % mod;
d.g = bc * n % mod * n1 % mod * i2 % mod;
d.h = bc * bc % mod * n1 % mod;
return d;
}
if (a >= c || b >= c){
d.f = n * n1 % mod * i2 % mod * ac % mod + bc * n1 % mod;
d.g = ac * n % mod * n1 % mod * n21 % mod * i6 % mod + bc * n % mod * n1 % mod * i2 % mod;
d.h = ac * ac % mod * n % mod * n1 % mod * n21 % mod * i6 % mod +
bc * bc % mod * n1 % mod + ac * bc % mod * n % mod * n1 % mod;
d.f %= mod, d.g %= mod, d.h %= mod;
node e = cal(n, a % c, b % c, c);
d.h += e.h + 2 * bc % mod * e.f % mod + 2 * ac % mod * e.g % mod;
d.g += e.g, d.f += e.f;
d.f %= mod, d.g %= mod, d.h %= mod;
return d;
}
else{
node e = cal(m - 1, c, c - b - 1, a);
d.f = n * m % mod - e.f, d.f = (d.f % mod + mod) % mod;
d.g = m * n % mod * n1 % mod - e.h - e.f, d.g = (d.g * i2 % mod + mod) % mod;
d.h = n * m % mod * (m + 1) % mod - 2 * e.g - 2 * e.f - d.f;
d.h = (d.h % mod + mod) % mod;
return d;
}
}
若只需要求f
ll cal(ll a,ll b,ll c,ll n){
if(!a){
return (n+1)*(b/c);
}
if(a>=c||b>=c){
return n*(n+1)/2*(a/c)+(n+1)*(b/c)+cal(a%c,b%c,c,n);
}
ll m=(a*n+b)/c;
return n*m-cal(c,c-b-1,a,m-1);
}
这两个板子本质上是一样的,只是第一个板子有取模(我懒得去掉 ),第二个板子只能够求f,第一个还能求g和h(详见oiwiki或洛谷)
回到这道题。题目意思就是要我们求有几个区间
[
A
+
B
i
,
A
+
C
i
]
[{A+Bi},{A+Ci}]
[A+Bi,A+Ci]内无d的倍数。
首先,根据鸽巢原理,我们可以知道一个长度为D的区间,一定会存在一点i,使得i mod d=0,所以,我们对区间的要求为:
(
A
+
C
i
)
−
(
A
+
B
i
)
+
1
≤
D
−
1
(A+Ci)-(A+Bi)+1 \le D-1
(A+Ci)−(A+Bi)+1≤D−1。变形为:
i
≤
D
−
2
C
−
B
i\le{D-2\over C-B}
i≤C−BD−2,那么,我们i的备选答案就有
k
=
D
−
2
C
−
B
k={D-2\over C-B}
k=C−BD−2个。
但是这些区间并不是全都符合要求的,只是区间长度符合了要求,区间内不一定符合。对于一个符合的区间,我们其实可以联想到
l
−
1
d
=
r
d
{l-1\over d}={r \over d}
dl−1=dr,因为我们知道,1~n内d的倍数有
n
d
n\over d
dn个,那么,一个区间内没有d的倍数,根据前缀和的思想,其实就是左右边界的d的倍数个数前缀和相等。
那么,对于备选的那些i,我们上面有保证了
区
间
长
度
≤
d
−
1
区间长度\le d-1
区间长度≤d−1,所以,我们可以这样统计那些不符合的区间:
∑
i
=
1
k
[
A
+
C
i
D
≠
A
+
B
i
−
1
D
]
=
∑
i
=
1
k
[
A
+
C
i
D
−
A
+
B
i
−
1
D
]
\sum_{i=1}^k[{A+Ci\over D}\neq{A+Bi-1\over D}]=\sum_{i=1}^k[{A+Ci\over D}-{A+Bi-1\over D}]
i=1∑k[DA+Ci=DA+Bi−1]=i=1∑k[DA+Ci−DA+Bi−1]
综上:我们的答案就是
k
−
∑
i
=
1
k
[
A
+
C
i
D
−
A
+
B
i
−
1
D
]
k-\sum_{i=1}^k[{A+Ci\over D}-{A+Bi-1\over D}]
k−∑i=1k[DA+Ci−DA+Bi−1],对于后面的式子,我们可以用类欧几里得求。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
void read(T&x){
ll f=1;
x=0;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f*=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+(ch-'0');
ch=getchar();
}
x*=f;
}
//=======================================================
#define int ll
int t,a,b,c,d;
ll cal(ll a,ll b,ll c,ll n){
if(!a){
return (n+1)*(b/c);
}
if(a>=c||b>=c){
return n*(n+1)/2*(a/c)+(n+1)*(b/c)+cal(a%c,b%c,c,n);
}
ll m=(a*n+b)/c;
return n*m-cal(c,c-b-1,a,m-1);
}
ll solve(){
ll k=(d-2)/(c-b);
auto p1=cal(c,a,d,k);
auto p2=cal(b,a-1,d,k);
return k-p1+p2+a/d-(a-1)/d;
}
signed main(){
//freopen("in.txt","r",stdin);
read(t);
while(t--){
read(a),read(b),read(c),read(d);
printf("%lld\n",solve());
}
return 0;
}