Problem D. Black Magic
题意:
给出n个方块,每个方块左右可能是黑色或者白色,如果两个相邻面都是黑色,这两个方块会连在一起,问最大和最小的连通块数量。
题解:
分类讨论,对于连通块最多的情况,要让能够相连的方块对尽量少。
可以发现,所有L可以全放在最左边,所有R可以全放在最右边,这样他们就不会和中间的方块相连,之后
尽可能用E把B隔开即可。对于连通块最少的情况,要让能够相连的方块对尽可能多。可以先把所有B拼在一起,之后可以把一个L和一个R分为一组拼起来。注意如果有至少一组LR并且也有至少一块B的话,可以把一组LR拆开拼在B的两边,这样可以再减少一个连通块。
代码:
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
int e, l, r, b;
void solve(){
cin >> e >> l >> r >> b;
if(b){
if(l + r) cout << e + 1 + max(l - 1, r - 1) << ' ';
else cout << e + 1 << ' ';
}
else {
if(l + r)cout << e + max(l, r) << ' ';
else cout << e << ' ';
}
if(b > e + 1) cout << e + l + r + b - (b - (e + 1)) << '\n';
else cout << e + l + r + b << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem H. Triangle Game
题意:
给出三角形的三条边,每个人每次可以选择一条边并减少这条边,不能操作的人输。问先手是否必胜。
题解:
每条边减一之后异或和不为零则为必胜。
代码:
#include<iostream>
using namespace std;
int a, b, c;
void solve(){
cin >> a >> b >> c;
if((a - 1) ^ (b - 1) ^ (c - 1)) cout << "Win\n";
else cout << "Lose\n";
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem C. Counting Stickmen
题意:
给定一张图,求符合要求的火柴人的数量。
题解:
先预处理可以做手、脚等的节点并记录有几种方案,最后组合数在去重即可。
代码:
#include<iostream>
#include<vector>
#define int long long
using namespace std;
const int N = 1e6 + 10;
const int mod = 998244353;
int n, u, v, ans;
int d[N], shou[N], tui[N];
vector<int> edg[N];
int C2(int x){
return (x * (x - 1) / 2) % mod;
}
void init(){
ans = 0;
for(int i = 1; i <= n; i++){
d[i] = tui[i] = shou[i] = 0;
edg[i].clear();
}
}
void dfs1(int u, int fa){
for(int v : edg[u]){
shou[u] += d[v] - 1;
if(d[v] >= 3) tui[u] += C2(d[v] - 1);
if(v == fa) continue;
dfs1(v, u);
}
}
void dfs2(int u, int fa){
for(int v : edg[u]){
if(d[v] >= 3 && d[u] >= 4) (ans += C2(d[v] - 1) * (C2(shou[u] - (d[v] - 1)) - (tui[u] - C2(d[v] - 1)) + mod) % mod * (d[u] - 3) % mod) %= mod;
if(v == fa) continue;
dfs2(v, u);
}
}
void solve(){
init();
cin >> n;
for(int i = 1; i < n; i++){
cin >> u >> v;
edg[u].push_back(v);
edg[v].push_back(u);
d[u] ++, d[v] ++;
}
dfs1(1, 0); dfs2(1, 0);
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem F. Sumire
题意:
计算一个式子
(式子太难打解释也很复杂,总而言之我很懒)
题解:
数位DP,用dp[i][j][0/1][0/1]表示已经统计了前i位,其中有j个位上的d,是否取数字上界,以及是否有前导零即可,用记忆化搜索维护转移。
代码:
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define SZ(x) ((int)x.size())
#define lowbit(x) x&-x
#define pb push_back
#define ALL(x) (x).begin(),(x).end()
#define UNI(x) sort(ALL(x)),x.resize(unique(ALL(x))-x.begin())
#define GETPOS(c,x) (lower_bound(ALL(c),x)-c.begin())
#define LEN(x) strlen(x)
#define MS0(x) memset((x),0,sizeof((x)))
#define Rint register int
#define ls (u<<1)
#define rs (u<<1|1)
typedef unsigned int unit;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef vector<int> Vi;
typedef vector<ll> Vll;
typedef vector<pii> Vpii;
template<class T> void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(ll &x) { scanf("%lld", &x); }
void _R(ull &x) { scanf("%llu", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { scanf(" %c", &x); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template<class T, class... U> void R(T &head, U &... tail) { _R(head); R(tail...); }
template<class T> void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template<class T,class U> void _W(const pair<T,U> &x) {_W(x.fi);putchar(' '); _W(x.se);}
template<class T> void _W(const vector<T> &x) { for (auto i = x.begin(); i != x.end(); _W(*i++)) if (i != x.cbegin()) putchar(' '); }
void W() {}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
const int MOD=1e9+7,mod=998244353;
ll qpow(ll a,ll b) {ll res=1;a%=MOD; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
const int MAXN=5e5+10,MAXM=1e7+10;
const int INF=INT_MAX,SINF=0x3f3f3f3f;
const ll llINF=LLONG_MAX;
const int inv2=(MOD+1)/2;
const int Lim=1<<20;
template <int _P>
struct Modint
{
static constexpr int P=_P;
private :
int v;
public :
Modint() : v(0){}
Modint(ll _v){v=_v%P;if(v<0)v+=P;}
explicit operator int() const {return v;}
explicit operator long long() const {return v;}
explicit operator bool() const {return v>0;}
bool operator == (const Modint &o) const {return v==o.v;}
bool operator != (const Modint &o) const {return v!=o.v;}
Modint operator - () const {return Modint(v?P-v:0);}
Modint operator + () const {return *this;}
Modint & operator ++ (){v++;if(v==P)v=0;return *this;}
Modint & operator -- (){if(v==0)v=P;v--;return *this;}
Modint operator ++ (int){Modint r=*this;++*this;return r;}
Modint operator -- (int){Modint r=*this;--*this;return r;}
Modint & operator += (const Modint &o){v+=o.v;if(v>=P)v-=P;return *this;}
Modint operator + (const Modint & o)const{return Modint(*this)+=o;}
Modint & operator -= (const Modint & o){v-=o.v;if(v<0)v+=P;return *this;}
Modint operator - (const Modint &o)const {return Modint(*this)-=o;}
Modint & operator *=(const Modint & o){v=(int)(((ll)v)*o.v%P);return *this;}
Modint operator * (const Modint & o)const {return Modint(*this)*=o;}
Modint & operator /= (const Modint & o){return (*this)*=o.Inv();}
Modint operator / (const Modint & o)const{return Modint(*this)/=o;}
friend Modint operator + (const Modint &x,const ll &o) {return x+(Modint)o;}
friend Modint operator + (const ll &o,const Modint &x) {return x+(Modint)o;}
friend Modint operator - (const Modint &x,const ll &o) {return x-(Modint)o;}
friend Modint operator - (const ll &o,const Modint &x) {return (Modint)o-x;}
friend Modint operator * (const Modint &x,const ll &o) {return x*(Modint)o;}
friend Modint operator * (const ll &o,const Modint &x) {return x*(Modint)o;}
friend Modint operator / (const Modint &x,const ll &o) {Modint c=o;return x*c.Inv();}
friend Modint operator / (const ll &o,const Modint &x) {Modint c=o;return c*x.Inv();}
Modint operator ^ (ll o)const{Modint r=1,t=v;while(o){if(o&1)r*=t;t*=t;o>>=1;}return r;}
Modint operator ~ (){return (*this)^(P-2);}
Modint Inv() const{return (*this)^(P-2);}
};
using mi=Modint<MOD>;
template<int P>
void _W(Modint<P> x){printf("%d",(int)x);}
template<int P>
void _R(Modint<P> &x){ll t;scanf("%lld",&t);x=t;}
mi dp[75][75][2][2],vis[75][75][2][2];;
ll t;
int s[75],k,b,d,n,m,c;
mi dfs(int dep,int tot,int lim,bool zero)
{
if(dep==m+1&&tot==0)return 1;
if(dep==m+1)return 0;
if(tot<0)return 0;
if(vis[dep][tot][lim][zero])return dp[dep][tot][lim][zero];
vis[dep][tot][lim][zero]=1;
int up=lim?s[dep]:b-1;
int ct=0,i=0;
int c=(i==d);
if(zero&&(d==0))c=0;
dp[dep][tot][lim][zero]+=dfs(dep+1,tot-c,lim&&(s[dep]==i),zero);
ct++;
if(i!=d&&d<=up)
{
ct++;
i=d;
int c=(i==d);
if(zero&&(d==0))c=0;
dp[dep][tot][lim][zero]+=dfs(dep+1,tot-c,lim&&(s[dep]==i),0);
}
if(i!=up)
{
ct++;
i=up;
dp[dep][tot][lim][zero]+=dfs(dep+1,tot,lim&&(s[dep]==i),zero&&(i==0));
}
dp[dep][tot][lim][zero]+=dfs(dep+1,tot,0,0)*max(0,up-ct+1);
return dp[dep][tot][lim][zero];
}
mi calc(bool f)
{
MS0(dp);MS0(vis);
R(t);
m=0;
t-=f;
while(t)
{
s[++m]=t%b;
t/=b;
}
reverse(s+1,s+m+1);
mi ans=0;
for(int i=1;i<=m;i++)
{
mi t=i;
ans+=dfs(1,i,1,1)*(t^k);
}
return ans;
}
void solve()
{
R(k,b,d);
mi ans=calc(1);
ans=calc(0)-ans;
W(ans);
}
int main()
{
srand(time(0));
int T=1;
scanf("%d",&T);
for(int kase=1;kase<=T;kase++)
{
//printf("Case #%d: ",kase);
solve();
}
return 0;
}
Problem B. Independent Feedback Vertex Set
题解:
答案必须包含每个三元环中的恰好一个点,因为一个点都不选则会破坏森林约束,选至少两个则会破坏
独立集约束。
同时对于一对有至少两个公共点的三元环,确定了答案包含其中一个的某个点之后另一个也随之确定了。
因此答案只可能有三种,分别对应图中唯一的三染色方案(去重后)中的每一种颜色的点。
代码:
#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
int n;
void solve(){
cin >> n;
vector<int> w(n), c(n, 0);
c[0] = 0, c[1] = 1, c[2] = 2;
for(int i = 0; i < n; i ++) cin >> w[i];
for(int i = 3; i < n; i ++){
int j, k;
cin >> j >> k;
j --, k --;
c[i] = (3 - c[j] - c[k]) % 3;
}
int ans[3] = {0, 0, 0};
for(int i = 0; i < n; i++) ans[c[i]] += w[i];
int res = max({ans[0], ans[1], ans[2]});
cout << res << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem I. Counting Good Arrays
题解:
考虑固定n和m,此时数组中都是m的因子,且最后一个位置为m。将每个ai除以ai-1之后对每个位置上的数做质因子分解,可以观察到答案对于每个质因子是互相独立的,即固定n时答案是一个关于m的积性函数。固定m时,设m中质因子p的幂为e,则方案数等于将e个p分给n-1个位置的方案数。显然这是一个组合数,且可以视作关于n的一个e次多项式。因此任选e+1个 ,对每个n用min25 算一次即可将答案插值出来。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 500001;
const int P = 1000000007;
typedef vector<int> vi;
inline int add(int a, int b) { int r = a + b; return r < P ? r : r - P; }
inline int sub(int a, int b) { int r = a - b; return r < 0 ? r + P : r; }
inline int mul(int a, int b) { return 1ll * a * b % P; }
inline int qpm(int a, int b) {
int r = 1;
do if (b & 1) r = mul(r, a);
while (a = mul(a, a), b >>= 1);
return r;
}
inline int inv(int x) { return qpm(x, P - 2); }
int invs[N], fac[N], ifac[N];
int binom(int n, int k) { return mul(fac[n], mul(ifac[n - k], ifac[k])); }
void ginv() {
invs[1] = 1; fac[0] = ifac[0] = 1;
for (int i = 2; i != N; ++i) invs[i] = mul(invs[P % i], (P - P / i));
for (int i = 1; i != N; ++i) fac[i] = mul(fac[i - 1], i);
for (int i = 1; i != N; ++i) ifac[i] = mul(ifac[i - 1], invs[i]);
}
int lintp(const vector<int>& x, const vector<int>& y, int k) {
int ans = 0, tt = x.size();
for (int i = 0; i != tt; ++i) {
if (k == x[i])
return y[i];
int u = 1, v = 1;
for (int j = 0; j != tt; ++j) {
if (i == j) continue;
u = mul(u, sub(k, x[j]));
v = mul(v, sub(x[i], x[j]));
}
ans = add(ans, mul(y[i], mul(u, inv(v))));
}
return ans;
}
namespace min25 {
const int N = 1000005;
bool ip[N]; vector<int> ps;
void sieve() {
fill_n(ip, N, 1); ip[1] = 0;
for (int i = 2; i < N; ++i) {
if (ip[i]) ps.push_back(i);
for (int j : ps) {
if (i * j >= N) break;
ip[i * j] = 0;
if (i % j == 0) break;
}
}
}
ll n, sq, w[N]; int c;
ll g[N];
inline int id(ll x) { return x ? x <= sq ? c - x + 1 : n / x : 0; }
void cal_g(ll n_) {
n = n_; sq = sqrt(n_); c = 0;
for (ll l = 1, r; l <= n; l = r + 1) {
ll v = w[++c] = n / l; r = n / v;
g[c] = (v - 1) % P;
}
for (int p : ps) {
if (1ll * p * p > n) break;
for (int j = 1; 1ll * p * p <= w[j]; ++j)
g[j] -= g[id(w[j] / p)] - g[id(p - 1)];
}
}
int cal_s(int n, int i, ll x) {
int p = ps[i];
if (x < p) return 0;
int sum = 0;
if (1ll * p * p > x)
sum = mul(n, sub(g[id(x)], i == 0 ? 0 : g[id(ps[i - 1])]));
else {
sum = cal_s(n, i + 1, x);
ll q = p;
for (int e = 1; q <= x; e++, q *= p)
sum = add(sum, mul(binom(n + e - 1, n - 1), add(cal_s(n, i + 1, x / q), 1)));
}
return sum;
}
int cal_sf(ll n, ll m) {
cal_g(m);
vector<int> x, y;
for (int i = 1; i <= 32; ++i) {
x.push_back(i);
y.push_back(add(y.empty() ? 0 : y.back(), add(cal_s(i, 0, m), 1)));
}
return lintp(x, y, n);
}
}
int main(void) {
ginv();
min25::sieve();
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d %d", &n, &m);
printf("%d\n", min25::cal_sf(n, m));
}
return 0;
}
原文地址:https://www.jb51.cc/wenti/3280194.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。