A.digits 2
直接输出 n n n个 n n n就好
#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int T,n,i;
int main()
{
cin>>T;
while (T--)
{
cin>>n;
fo(i,1,n) cout<<n;
cout<<endl;
}
return 0;
}
B.generator 1
没想到真的是裸的快速幂。。
不过这里要用以
10
10
10为底
比如
e
234
=
(
e
100
)
2
∗
(
e
10
)
3
∗
(
e
1
)
4
e^{234}=(e^{100})^2*(e^{10})^3*(e^1)^4
e234=(e100)2∗(e10)3∗(e1)4
所以
e
e
e每次乘
10
10
10次,
a
n
s
ans
ans每次乘若干次
这里还有几个优化:
1、计算
e
e
e的
10
10
10次可以用二进制做法(
e
10
=
e
8
∗
e
2
e^{10}=e^8*e^2
e10=e8∗e2)
2、计算
a
n
s
ans
ans也可以用二进制做法
(
e
100
)
6
=
(
e
100
)
4
∗
(
e
100
)
2
(e^{100})^6=(e^{100})^4*(e^{100})^2
(e100)6=(e100)4∗(e100)2
如果不用这个优化可能会T(按照题解的意思)(似乎还有人更慢的233333)
不过常数优秀的话暴力算也没啥问题(卡这个就没意思了吧)
这个题如果暴力转换二进制估计就走远了。。就算能写出来也是T?
不过还有数学大佬大力手推循环节真的nb嗷
(其实循环节也很好想,
a
n
−
1
a_{n-1}
an−1和
a
n
−
2
a_{n-2}
an−2作为二元组最多也就
p
2
p^2
p2种取法,一旦重复就会产生循环节)
C.generator 2
利用高中数列知识我们可以求出数列的通项公式(利用特征根法)
注意这里要特判掉
a
=
1
a=1
a=1的情况
然后经过一系列变形之后可以得到一个
a
x
≡
b
(
m
o
d
a^x \equiv b(mod
ax≡b(mod
p
)
p)
p)的方程
这是一个
B
S
G
S
BSGS
BSGS算法的裸题
当然这里要用到神奇的预处理。。
假设需要解的方程为
a
x
≡
b
a^x \equiv b
ax≡b,首先改写为
a
x
t
1
−
t
2
≡
b
a^{xt_1-t_2} \equiv b
axt1−t2≡b
等价于
a
x
t
1
≡
b
a
t
2
a^{xt_1} \equiv ba^{t_2}
axt1≡bat2,其中
t
1
=
1000
,
t
2
<
1000
t_1=1000,t_2<1000
t1=1000,t2<1000
然后可以把左边全部先预处理出来,然后枚举
t
2
t_2
t2,用各种做法(二分、哈希、玄学)把所有解搜出来就好了
F.maximum clique 1
这个图的补图是一个二分图。。
(我都想到这个图转换成补图似乎有点意思,可是就是没想到是二分图)
这还是离散考试的内容。。仅有1位二进制不同的点互相连边组成的图是二分图,就是按照1的个数的奇偶性划分
然后。。就没了,裸的二分图最大独立集
#include <iostream>
#include <vector>
#include <cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int N=100050;
const int M=100050;
int a[10000],f[10000],g[10000];
int x,num,i,j,nn;
int n,m;
struct Edge{
int v,next;
}edge[M];
int cnt,head[N];
void init(){
cnt=0;
memset(head,-1,sizeof(head));
}
void addEdge(int u,int v){
edge[cnt]=Edge{v,head[u]};
head[u]=cnt++;
}
//Left表示右边点集的左边匹配点
int Left[N],Right[N];
//S T分别表示左右点集是否已匹配
bool S[N],T[N];
//X Y表示左右两边的最小覆盖点集
vector<int> X,Y;
bool dfs(int u){
S[u]=true;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(!T[v]){
T[v]=true;
if(Left[v]==-1 || dfs(Left[v])){
Left[v]=u;
Right[u]=v;
return true;
}
}
}
return false;
}
int ans;
void solve(){
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
memset(Left,-1,sizeof(Left));
memset(Right,-1,sizeof(Right));
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
//左边没有匹配的点再增广下去
for(int i=1;i<=n;i++){
if(Right[i]==-1){
dfs(i);
}
}
//此时最小点覆盖集即是左边没标记的点
for(int u=1;u<=n;u++){
if(!S[u]){
X.push_back(u);
}
}
//和右边已标记的点
for(int v=1;v<=m;v++){
if(T[v]){
Y.push_back(v);
}
}
}
int main(){
init();
scanf("%d",&nn);
fo(i,1,nn) scanf("%d",&a[i]);
n = m = 0;
fo(i,1,nn)
{
x = a[i]; num = 0;
while (x)
{
if (x&1) num++; x >>= 1;
}
if (num % 2 == 0) f[++n] = a[i]; else g[++m] = a[i];
}
fo(i,1,n)
fo(j,1,m)
{
x = f[i] ^ g[j]; num = 0;
while (x) {num += x & 1; x >>= 1;}
if (num == 1) addEdge(i,j);
}
solve();//最小点覆盖集,补集即是最大独立集
int ans = nn-(X.size()+Y.size());
printf("%d\n",ans);
for(int i=0;i<X.size();i++) f[X[i]] = -1;
for(int i=0;i<Y.size();i++) g[Y[i]] = -1;
fo(i,1,n) if (f[i] != -1) cout<<f[i]<<" ";
fo(i,1,m) if (g[i] != -1) cout<<g[i]<<" ";
cout<<endl;
return 0;
}
G.subsequence 1
首先用
d
p
dp
dp找出长度相等的子序列
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
s
s
s串匹配到第
i
i
i位,
t
t
t串匹配到第
j
j
j位,并且目前还等于的方案数
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示
s
s
s串匹配到第
i
i
i位,
t
t
t串匹配到第
j
j
j位,并且已经严格大于的方案数
n
2
n^2
n2转移一下就好
然后用组合数找出长度比
t
t
t长的情况
#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define MOD 998244353
#define N 3005
using namespace std;
int T,n,m,i,j,l;
long long res;
long long a[N],b[N],f[N],g[N],f_t[N],g_t[N],c[N][N];
char ch[N];
void init()
{
c[0][0] = 1;
fo(i,1,3000)
{
c[i][0] = 1;
fo(j,1,i) c[i][j] = (c[i-1][j-1] + c[i-1][j]) % MOD;
}
}
int main()
{
init();
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
scanf("%s",ch+1); fo(i,1,n) a[i] = ch[i] - '0';
scanf("%s",ch+1); fo(i,1,m) b[i] = ch[i] - '0';
res = 0;
fo(i,1,n)
if (a[i] != 0)
{
fo(j,m,n-i)
res = (res + c[n-i][j]) % MOD;
}
fo(i,0,n) f[i] = g[i] = f_t[i] = g_t[i] = 0;
fo(i,1,n)
{
f[0] = 1;
fo(j,1,m)
{
if (j == 1 && a[i] == 0) continue;
if (a[i] == b[j]) f_t[j] = (f_t[j] + f[j-1]) % MOD;
if (a[i] > b[j]) g_t[j] = (g_t[j] + f[j-1]) % MOD;
g_t[j] = (g_t[j] + g[j-1]) % MOD;
}
fo(j,0,m)
{
f[j] = (f[j] + f_t[j]) % MOD;
g[j] = (g[j] + g_t[j]) % MOD;
f_t[j] = g_t[j] = 0;
}
}
res = (res + g[m]) % MOD;
cout<<res<<endl;
}
return 0;
}
H.subsequence 2
大力猜测如果有答案则唯一(其实不唯一也没关系吧。。)
这个题如果你想到了拓扑排序就很好做。。因为每次实际上是给了你若干个点的相对顺序
比如
a
a
a
b
b
a
aaabba
aaabba,实际上可以看成是
a
1
a
2
a
3
b
1
b
2
a
4
a_1a_2a_3b_1b_2a_4
a1a2a3b1b2a4
当跑拓扑的时候出现环(其实就是最后没跑完)那么说明无解
注意这里的无解情况有很多种,不仅仅只有出现次数不一致,还有可能是顺序非法(其实直接判断拓扑结果就好了)
#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define MOD 998244353
#define N 30005
using namespace std;
int n,m,t,i,j,num1,num2,k,nw,nt;
int len[N],x[N],y[N],num[N],color[N],f[N],edgein[N];
char s[N],ch[N],res[N];
vector<int> chh[N],id[N],e[N];
queue<int> q;
int main()
{
scanf("%d%d",&n,&m);
t = m * (m - 1) / 2;
fo(i,1,t)
{
scanf("%s%d",s+1,&len[i]);
gets(ch+1);
gets(ch+1);
fo(j,1,len[i]) chh[i].push_back(ch[j]-'a'+1);
x[i] = s[1] - 'a' + 1; y[i] = s[2] - 'a' + 1;
num1 = num2 = 0;
fo(j,1,len[i]) if (ch[j] -'a' + 1 == x[i]) num1++; else num2++;
if (num[x[i]] == 0 || num[x[i]] == num1)
num[x[i]] = num1; else {printf("-1\n"); return 0;}
if (num[y[i]] == 0 || num[y[i]] == num2)
num[y[i]] = num2; else {printf("-1\n"); return 0;}
}
num1 = 0;
fo(i,1,m) num1 += num[i];
if (num1 != n) {printf("-1\n"); return 0;}
k = 0;
fo(i,1,m)
fo(j,1,num[i])
{
k++;
id[i].push_back(k);
color[k] = i;
}
fo(i,1,t)
{
num1 = num2 = -1;
fo(j,1,len[i])
{
if (chh[i][j-1] == x[i])
{
num1++; f[j] = id[x[i]][num1];
} else
{
num2++; f[j] = id[y[i]][num2];
}
}
fo(j,1,len[i]-1) {e[f[j]].push_back(f[j+1]); edgein[f[j+1]]++;}
}
fo(i,1,k) if (edgein[i] == 0) q.push(i);
k = 0;
while (!q.empty())
{
nw = q.front(); q.pop();
k++; res[k] = color[nw]-1+'a';
for (i = 0;i < e[nw].size(); i++)
{
nt = e[nw][i];
edgein[nt]--;
if (edgein[nt] == 0) q.push(nt);
}
}
if (k != n) printf("-1"); else
fo(i,1,k) printf("%c",res[i]);
printf("\n");
return 0;
}
I.three points I
首先大力猜测通过各种变换,三角形的其中一个顶点一定可以和矩形的角重合
然后暴力枚举哪条边卡在边界上(也就是枚举第二个点在边界的位置)
然后就可以算出第三个点的位置
注意1:第二个点的位置有可能有两种情况(有可能在右下,有可能在左上)
哦不过针对这个问题,我的做法是将坐标关于
y
=
x
y=x
y=x作对称然后再算一遍就好了(就是把矩形的长宽对调一下)
注意2:第三个点的位置有四种情况(直线的两侧各两种)
这里有一个技巧,不需要每次复杂地讨论解是否合法,直接把三个点算出来
c
h
e
c
k
check
check一下就好(注意
e
p
s
eps
eps)
这个题最坑的就是输出要和输入对应,如果编程习惯优秀的话可能没啥问题
而我选择暴力枚举六种情况。。。
ps赛场上狂wa的原因是,我们默认了卡在边界上的边是最长的。。实际上三条边都有可能卡在边界上
#include <bits/stdc++.h>
#define PI acos(-1.0)
#define eps 1e-8
using namespace std;
long double w, h, a, b, c ,x,y,z;
long double ax, ay, bx, by, cx, cy;
long double ang1, ang2, ang3, ang4;
bool pos_check(){
if (ax - w <= eps && ax >= -eps && bx - w <= eps && bx >= -eps
&& ay - h <= eps && ay >= -eps && by - h <= eps && by >= -eps) return true;
return false;
}
bool check(long double a,long double b,long double c){/*
if (a < b) swap(a, b);
if (a < c) swap(a, c);*/
long double A = acos( (c*c+b*b-a*a)/(2*b*c) );
// 1
if (a <= w){
ax = a, ay = 0;
ang1 = 0;
}
else{
ax = w, ay = sqrt(a*a - w*w);
ang1 = ang1 = acos(w/a);;
}
ang2 = acos( (a*a+b*b-c*c) / (2*a*b) );
ang3 = ang2 + ang1;
bx = b*cos(ang3);
by = b*sin(ang3);
if (pos_check()) return true;
// 2
ang3 = ang1 - ang2;
bx = b*cos(ang3);
by = b*sin(ang3);
if (pos_check()) return true;
// 3
swap(b, c);
ang2 = acos( (a*a+b*b-c*c) / (2*a*b) );
ang3 = ang2 + ang1;
bx = b*cos(ang3);
by = b*sin(ang3);
if (pos_check()) return true;
// 4
ang3 = ang1 - ang2;
bx = b*cos(ang3);
by = b*sin(ang3);
if (pos_check()) return true;
return false;
}
bool checkkkk(long double a,long double b,long double c,long double d,long double e,long double f)
{
long double s1 = sqrt((a-c)*(a-c)+(b-d)*(b-d));
long double s2 = sqrt((a-e)*(a-e)+(b-f)*(b-f));
long double s3 = sqrt((c-e)*(c-e)+(d-f)*(d-f));
if (fabs(s1-x) <= eps && fabs(s2-y) <= eps && fabs(s3-z) <= eps)
{
printf("%.12Lf %.12Lf %.12Lf %.12Lf %.12Lf %.12Lf\n", a, b, c, d, e, f);
return true;
}
return false;
}
bool check2(int op,long double a,long double b,long double c)
{
bool flag;
if (op == 0) flag = check(a,b,c);
else
{
swap(w,h);
flag = check(a,b,c);
swap(ax,ay); swap(bx,by); swap(cx,cy);
swap(w,h);
}
if (!flag) return false;
//check6种对应关系
if (checkkkk(ax, ay, bx, by, cx, cy)) return true;
if (checkkkk(ax, ay, cx, cy, bx, by)) return true;
if (checkkkk(bx, by, ax, ay, cx, cy)) return true;
if (checkkkk(bx, by, cx, cy, ax, ay)) return true;
if (checkkkk(cx, cy, ax, ay, bx, by)) return true;
if (checkkkk(cx, cy, bx, by, ax, ay)) return true;
return false;
}
int main () {
int T;
scanf("%d", &T);
while(T--) {
scanf("%Lf%Lf%Lf%Lf%Lf", &w, &h, &a, &b, &c);
x = a; y = b; z = c;
//枚举长宽以及卡在边界上的边
if (check2(0,a,b,c)) continue;
if (check2(1,a,b,c)) continue;
if (check2(0,b,a,c)) continue;
if (check2(1,b,a,c)) continue;
if (check2(0,c,a,b)) continue;
if (check2(1,c,a,b)) continue;
}
return 0;
}