2022NOIPA层联测29
快乐一下:为了kuangbiaopilihu的形象我把它删掉了。
今天的T3的“部分分”不一定是假做法,比如我写的其它部分是正解好不容易记住了求前缀和,但是求了一个任意两点间最短路?!,而且特判了一个m<=10的小数据枚举排列打算保底结果保没了10pts,60->50
还有今天的T2,能想起来拓展欧拉定理(赛后才知道把指数和欧拉函数取模还有这么个高级的名字),也能想起来欧拉函数的求法,但是面对这么大的数据不知道怎么读入?!,于是就写了个10分的走人了……
我是个什么der……
A. A
状压dp,状态是这个后缀和有没有出现过,只记录有可能做贡献的后缀和,太大的都扔掉,出现过对应的二进制位就是1。
为了去重,第一次出现合法后缀后不再转移,加入一个新的数字就代表之前出现过的从0开始的所有后缀和都变大了,把“0位置”初始化成1可以在左移的时候直接添上新数的贡献。整体左移一位的写法看起来很美观好像对时间影响也不大就不改了。
max({p,q,r})这种写法头一次见真的可以过!?所以这东西NOIP能用吗,我不知道……
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 2;
const int mod = 998244353;
int f[55][1<<20], n, m, p, q, r, x, all, ans;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
inline ll qpow(ll a, ll b)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void add(int &x, int y) {x+=y; if(x>=mod) x-=mod;}
int main()
{
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
n = read(), m = read();
p = read(), q = read(), r = read();
x = p + q + r; all = (1<<x+1)-1;
f[0][1] = 1;
for(int i=0; i<n; i++)
{
for(int j=0; j<=all; j++)
{
if(!f[i][j]) continue;
if((j&(1<<r))&&(j&(1<<q+r))&&(j&(1<<p+q+r))) continue;
for(int k=1; k<=min(m,max({p,q,r})); k++) add(f[i+1][((j<<k)&all)^1], f[i][j]);
if(m > max({p,q,r})) add(f[i+1][1], 1ll*f[i][j]*(m-max({p,q,r}))%mod);
}
}
for(int i=1; i<=n; i++) for(int j=0; j<=all; j++)
if((j&(1<<r))&&(j&(1<<q+r))&&(j&(1<<p+q+r))) add(ans, 1ll*f[i][j]*qpow(m,n-i)%mod);
printf("%d\n", ans);
return 0;
}
B. B
找个美观的拓展欧拉定理放一下qwq,(b-1)*bn-1。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
ll mod, ans;
ll n, b, br, ur, e1, e2, sq;
char ns[maxn], bs[maxn];
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
inline ll qpow(ll a, ll b)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
ll phi(ll n)
{
ll ans = n, sq = sqrt(n);
for(ll i=2; i<=sq; i++)
{
if(n % i == 0)
{
ans = ans / i * (i-1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = ans / n * (n-1);
return ans;
}
int main()
{
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
scanf("%s%s", ns+1, bs+1); mod = read();//啊这,我读入读反了??
e1 = strlen(bs+1), e2 = strlen(ns+1);
for(int i=1; i<=e1; i++)
{
b = (b * 10 % mod + bs[i] - '0') % mod;
}
ur = phi(mod);
bool flag = 0;
for(int i=1; i<=e2; i++)
{
n = (n * 10 + ns[i] - '0');
if(n > mod) flag = 1;
if(flag) n %= ur;
}
if(flag) n = (n - 1) % ur + ur;
else n--;
ans = 1;
ans = (b - 1 + mod) % mod;
ll res3 = qpow(b, n);
ans = ans * res3 % mod;
printf("%lld\n", ans);
return 0;
}
C. C
关于我为什么写了这么多dij而不直接把参数传进去,我也不知道当时怎么想的……一个确定了3个起点还去跑任意两点间最短路,拿T2的代码试T1的样例调不出来还以为是自己求欧拉函数求锅了的大Sily Cat。
CR:你是以什么精神状态在做题啊??
Cat:……
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 2;
const int mod = 998244353;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, m, p1, p2, p3;
ll d1[maxn], d2[maxn], d3[maxn], ans=inf, w[maxn];
bool vis[maxn];
typedef pair<ll, int> pii;
priority_queue<pii, vector<pii>, greater<pii> > q;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct node
{
int nxt, to, w;
}a[maxn<<1];
int head[maxn], len;
void add(int x, int y, int w)
{
a[++len].to = y; a[len].nxt = head[x]; a[len].w = w;
head[x] = len;
}
struct node2
{
int u, v;
}e[maxn];
void dij1(int s)
{
for(int i=1; i<=n; i++) d1[i] = inf;
for(int i=1; i<=n; i++) vis[i] = 0;
d1[s] = 0;
q.push(pii(d1[s], s));
while(!q.empty())
{
int x = q.top().second; q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i=head[x]; i; i=a[i].nxt)
{
int y = a[i].to;
if(d1[y] > d1[x] + a[i].w)
{
d1[y] = d1[x] + a[i].w;
q.push(pii(d1[y], y));
}
}
}
}
void dij2(int s)
{
for(int i=1; i<=n; i++) d2[i] = inf;
for(int i=1; i<=n; i++) vis[i] = 0;
d2[s] = 0;
q.push(pii(d2[s], s));
while(!q.empty())
{
int x = q.top().second; q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i=head[x]; i; i=a[i].nxt)
{
int y = a[i].to;
if(d2[y] > d2[x] + a[i].w)
{
d2[y] = d2[x] + a[i].w;
q.push(pii(d2[y], y));
}
}
}
}
void dij3(int s)
{
for(int i=1; i<=n; i++) d3[i] = inf;
for(int i=1; i<=n; i++) vis[i] = 0;
d3[s] = 0;
q.push(pii(d3[s], s));
while(!q.empty())
{
int x = q.top().second; q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i=head[x]; i; i=a[i].nxt)
{
int y = a[i].to;
if(d3[y] > d3[x] + a[i].w)
{
d3[y] = d3[x] + a[i].w;
q.push(pii(d3[y], y));
}
}
}
}
int fa[maxn], dep[maxn], siz[maxn], son[maxn], top[maxn];
void find_heavy_edge(int u, int fat, int depth)
{
fa[u] = fat;
dep[u] = depth;
siz[u] = 1;
son[u] = 0;
int maxsize = 0;
for(int i=head[u]; i; i=a[i].nxt)
{
int v = a[i].to;
if(v == fat) continue;
find_heavy_edge(v, u, depth+1);
siz[u] += siz[v];
if(siz[v] > maxsize)
{
maxsize = siz[v];
son[u] = v;
}
}
}
void connect_heavy_edge(int u, int ancestor)
{
top[u] = ancestor;
if(son[u])
{
connect_heavy_edge(son[u], ancestor);
}
for(int i=head[u]; i; i=a[i].nxt)
{
int v = a[i].to;
if(v == son[u] || v == fa[u]) continue;
connect_heavy_edge(v, v);
}
}
int LCA(int x, int y)
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
return x;
}
void solve2()
{
find_heavy_edge(p1, p1, 1);
connect_heavy_edge(p1, p1);
int cnt1 = dep[p3] - 1, lca = LCA(p2, p3);
int cnt2 = dep[p2] - dep[lca];
sort(w+1, w+1+m);
for(int i=2; i<=m; i++) w[i] += w[i-1];
ans = w[cnt2] + w[cnt2+cnt1];
printf("%lld\n", ans);
exit(0);
}
int main()
{
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
n = read(); m = read();
p1 = read(), p2 = read(), p3 = read();
for(int i=1; i<=m; i++)
{
int u = read(), v = read(); w[i] = read();
add(u, v, 1); add(v, u, 1);
}
if(m == n - 1) solve2();
sort(w+1, w+1+m);
for(int i=2; i<=m; i++) w[i] += w[i-1];
dij1(p1), dij2(p2), dij3(p3);
for(int i=1; i<=n; i++)
{
if(d1[i] + d2[i] + d3[i] > m) continue;
ll res = w[d2[i]] + w[d2[i]+d1[i]+d3[i]];
ans = min(ans, res);
}
printf("%lld\n", ans);
return 0;
}
D. D
我才知道原来任意的图都可以一笔覆盖偶数次,把一条边看成两条那么所有点的度数都是偶数了。于是偶数的情况就是连通块个数-1,奇数的话可以画偶数次回到原点这条线不断,等于直接考虑m=1的情况。
如果经过回路所有点度数-2,如果经过的不是回路端点度数-1其它点度数-2,奇点个数减少两个。
APJ:
假如我们有 n 个奇点,那么至少需要经过 n / 2 条路径才能使所有的奇点全变为偶点,所有点都变成偶点后就可以直接一次欧拉回路削完所有边了。
考虑如果有两条路径重合,我们可以将两条路径的一个端点交换,这样就可以使两条路径不相交,这样做若干次后一定可以使每两条路径不相交,那么就可以削完了。
关于/2是不是会向下取整,其实奇点个数不可能有奇数个。
建图的话把覆盖的都合并,交叉的都建出新点,感觉mod mod mod Chen_jr的实现方式是最简洁的!!%%%%%%%%%%
code
//鹤了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2005;
const int mod = 998244353;
//这个神奇的数组大小我很不能理解
int n, m, tot, deg[maxn*maxn*10], val[maxn*maxn*10], ans, f[maxn*maxn*10];
bool del[maxn];
typedef pair<int, int> pii;
map<pii, int> id;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct Line
{
int x1, x2, y;
bool operator < (const Line &T) const
{
return y < T.y;
}
}h[maxn], z[maxn];
int ch, cz;
bool cover(Line L, Line R) {return L.y == R.y && ((L.x1>=R.x1&&L.x2<=R.x2)||(L.x2>=R.x1&&L.x2<=R.x2));}
bool check(Line H, Line Z) {return H.x1 <= Z.y && H.x2 >= Z.y && Z.x1 <= H.y && Z.x2 >= H.y;}
void uniqu(Line a[], int &len)
{
int nlen = 0;
for(int i=1; i<=len; i++) del[i] = 0;
for(int i=1; i<=len; i++)
{
for(int j=1; j<i; j++) if(!del[j] && (cover(a[i], a[j]) || cover(a[j], a[i])))
{
a[i].x1 = min(a[i].x1, a[j].x1);
a[i].x2 = max(a[i].x2, a[j].x2);
del[j] = 1;
}
}
for(int i=1; i<=len; i++) if(!del[i]) a[++nlen] = a[i];
len = nlen;
}
void New(pii x)
{
id[x] = ++tot; f[tot] = tot;
}
int fa(int x) {return x == f[x] ? x : f[x] = fa(f[x]);}
void merge(int x, int y)
{
x = fa(x), y = fa(y); if(x == y) return;
f[x] = y;
}
void link(int u, int v)
{
deg[u]++; deg[v]++; merge(u, v);
}
void LINK()
{
for(int i=1; i<=ch; i++)
{
pii now = pii(h[i].x1, h[i].y);
if(!id[now]) New(now); int las = id[now];
for(int j=1; j<=cz; j++) if(check(h[i], z[j]))
{
pii now = pii(z[j].y, h[i].y);
if(!id[now]) New(now);
if(las != id[now]) link(las, id[now]);
las = id[now];
}
now = pii(h[i].x2, h[i].y);
if(!id[now]) New(now);
if(las != id[now]) link(las, id[now]);
}
for(int i=1; i<=cz; i++)
{
pii now = pii(z[i].y, z[i].x1);
if(!id[now]) New(now); int las = id[now];
for(int j=1; j<=ch; j++) if(check(h[j], z[i]))
{
pii now = pii(z[i].y, h[j].y);
if(!id[now]) New(now);
if(las != id[now]) link(las, id[now]);
las = id[now];
}
now = pii(z[i].y, z[i].x2);
if(!id[now]) New(now);
if(las != id[now]) link(las, id[now]);
}
}
void CUT()
{
ans = -1;
for(int i=1; i<=tot; i++) ans += fa(i) == i;
if(!(m & 1)) return;
for(int i=1; i<=tot; i++) val[fa(i)] += (deg[i] & 1);
for(int i=1; i<=tot; i++) if(fa(i) == i) ans += max(val[i]/2-1, 0);
}
int main()
{
freopen("d.in", "r", stdin);
freopen("d.out", "w", stdout);
n = read(); m = read();
for(int i=1; i<=n; i++)
{
int a = read(), b = read(), c = read(), d = read();
if(a == c) z[++cz] = {min(b,d), max(b,d), a};
else h[++ch] = {min(a,c), max(a,c), b};
}
uniqu(z, cz); uniqu(h, ch); sort(h+1, h+1+ch); sort(z+1, z+1+cz);
LINK(); CUT();
printf("%d\n", ans);
return 0;
}