网址
2021CCPC威海
赛时过题与罚时

A.Goodbye, Ziyin!
签到题,队友写的
#include<bits/stdc++.h>
using namespace std;
int cnt[10], de[1000010];
int main() {
	int n;
	cin >> n;
	for(int i = 1; i < n; ++i) {
		int u, v;
		scanf("%d %d", &u, &v);
		++de[u];
		++de[v];
	}
	for(int i = 1; i <= n; ++i) {
		if(de[i] > 3) {
			cout << "0";
			return 0;
		}
		cnt[de[i]]++;
	}
	cout << cnt[1] + cnt[2];
	return 0;
}
 
D.Period
队友写的KMP,签到题
#include <bits/stdc++.h>
using namespace std;
#define MAXN 1000005
char s[MAXN];
int nxt[MAXN];
int n;
int ans[MAXN];
int main()
{
    scanf("%s", s + 1);
    n = strlen(s + 1);
    int j = 0;
    for(int i = 2; i <= n; ++i)
    {
        while(j && s[i] != s[j + 1]) j = nxt[j];
        if(s[i] == s[j + 1]) j++;
        nxt[i] = j;
    }
    /* for(int i = 1; i <= n; ++i)
     * {
     *     cout << nxt[i] << " ";
     * }
     * cout << "\n"; */
    j = nxt[n];
    while(j)
    {
        int l = j;
        int r = n - l + 1;
        if(r > l)
        {
            ans[r] --;
            ans[l + 1] ++;
        }
        j = nxt[j];
    }
    for(int i = 1; i <= n; ++i)
        ans[i] = ans[i] + ans[i-1];
    /* for(int i = 1; i <= n; ++i)
     * {
     *     cout << ans[i] << " ";
     * }
     * cout << "\n"; */
    int q;
    scanf("%d", &q);
    while(q--)
    {
        int l;
        scanf("%d", &l);
        printf("%d\n", ans[l]);
    }
    return 0;
}
 
G. Shinyruo and KFC
简单组合计数
 若 
     
      
       
       
         k 
        
       
         < 
        
       
         m 
        
       
         a 
        
       
         x 
        
       
         ( 
        
        
        
          a 
         
        
          i 
         
        
       
         ) 
        
       
      
        k<max(a_i) 
       
      
    k<max(ai),显然无解
 有解时答案是 
     
      
       
        
        
          ∏ 
         
         
         
           i 
          
         
           = 
          
         
           1 
          
         
        
          n 
         
        
       
         C 
        
       
         ( 
        
       
         k 
        
       
         , 
        
        
        
          a 
         
        
          i 
         
        
       
         ) 
        
       
         = 
        
        
        
          ∏ 
         
         
         
           i 
          
         
           = 
          
         
           1 
          
         
        
          n 
         
        
        
         
         
           k 
          
         
           ! 
          
         
         
          
          
            a 
           
          
            i 
           
          
         
           ! 
          
         
           ∗ 
          
         
           ( 
          
         
           k 
          
         
           − 
          
          
          
            a 
           
          
            i 
           
          
         
           ) 
          
         
           ! 
          
         
        
       
      
        \prod_{i=1}^{n} C(k,a_i)=\prod_{i=1}^{n} \frac{k!}{{a_i}!*(k-a_i)!} 
       
      
    ∏i=1nC(k,ai)=∏i=1nai!∗(k−ai)!k!
 这样显然有一个 
     
      
       
       
         O 
        
       
         ( 
        
       
         m 
        
       
         n 
        
       
         ) 
        
       
      
        O(mn) 
       
      
    O(mn)的做法
 观察数据性质 
     
      
       
        
        
          ∑ 
         
         
         
           i 
          
         
           = 
          
         
           1 
          
         
        
          n 
         
        
        
        
          a 
         
        
          i 
         
        
       
         < 
        
       
         = 
        
       
         1 
        
       
         e 
        
       
         5 
        
       
      
        \sum_{i=1}^{n}a_i<=1e5 
       
      
    ∑i=1nai<=1e5
 即 
     
      
       
        
        
          a 
         
        
          i 
         
        
       
      
        a_i 
       
      
    ai的种类数量级别为 
     
      
       
       
         s 
        
       
         q 
        
       
         r 
        
       
         t 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        sqrt(n) 
       
      
    sqrt(n)
 那么就可以把相同的 
     
      
       
        
        
          a 
         
        
          i 
         
        
       
      
        a_i 
       
      
    ai一起做,优化成 
     
      
       
       
         O 
        
       
         ( 
        
       
         m 
        
        
        
          n 
         
        
       
         ) 
        
       
      
        O(m\sqrt{n}) 
       
      
    O(mn)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
const int MOD = 998244353;
int n, m, a[N];
ll b[N];
ll c[N];
ll cnt[N];
ll power(ll x, ll y) {
	if(y == 0) {
		return 1;
	}
	ll res = 1;
	while(y)
	{
		if(y&1) res = res * x % MOD;
		x  = x * x % MOD; y >>= 1;
	}
	return res;
}
ll ni(ll x) {
	return power(x, MOD - 2);
}
int main() {
	scanf("%d %d", &n, &m);
	b[0] = 1;
	for(ll i = 1; i <= 1e5; ++i) {
		b[i] = b[i - 1] * i % MOD;
		c[i] = power(b[i], n);
	}
	int mx = 0;
	for(ll i = 1; i <= n; ++i ) {
		scanf("%d", &a[i]);
		++cnt[a[i]];
		mx = max(mx, a[i]);
	}
	sort(a + 1, a + 1 + n);
	n = unique(a + 1, a + 1 + n) - (a + 1);
	for(int k = 1; k <= m; ++k) {
		if(k < mx) {
			puts("0");
			continue;
		}
		ll ans = c[k];
		for(ll i = 1; i <= n; ++i) {
			ll t = b[a[i]] * b[k - a[i]] % MOD;
			t = power(t, cnt[a[i]]);
			ans = ans * ni(t) % MOD;
		}
		printf("%lld\n", ans);
	}
	return 0;
}
 
H. city safet
发现 
     
      
       
       
         n 
        
       
      
        n 
       
      
    n的范围很像网络流,然后就往这方面想,后面发现每当多选择一个距离相当于是获得了 
     
      
       
        
        
          v 
         
         
         
           i 
          
         
           + 
          
         
           1 
          
         
        
       
         − 
        
        
        
          v 
         
        
          i 
         
        
       
      
        v_{i+1}-v_{i} 
       
      
    vi+1−vi的贡献,每当修复了一个点,相当于是花费了 
     
      
       
        
        
          w 
         
        
          i 
         
        
       
      
        w_i 
       
      
    wi,我们要统一边权的性质,即我们可以想办法把问题变成最大贡献或者最小花费
 那么我们可以假设初始时全部修复,
 那么每个点建立 
     
      
       
       
         n 
        
       
      
        n 
       
      
    n个代表点, 
     
      
       
       
         i 
        
        
        
          d 
         
         
         
           i 
          
         
           , 
          
         
           j 
          
         
        
       
      
        id_{i,j} 
       
      
    idi,j表示以点 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i为中心,距离 
     
      
       
       
         < 
        
       
         = 
        
       
         j 
        
       
      
        <=j 
       
      
    <=j的一类点,共 
     
      
       
       
         n 
        
       
         ∗ 
        
       
         n 
        
       
      
        n*n 
       
      
    n∗n个代表点
 然后建立 
     
      
       
       
         n 
        
       
      
        n 
       
      
    n个具体点 
     
      
       
       
         1 
        
       
      
        1 
       
      
    1到 
     
      
       
       
         n 
        
       
      
        n 
       
      
    n
 对于建图而言,
 第 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i个点的代表点 
     
      
       
       
         j 
        
       
      
        j 
       
      
    j向代表点 
     
      
       
       
         j 
        
       
         − 
        
       
         1 
        
       
      
        j-1 
       
      
    j−1连边, 
     
      
       
       
         ( 
        
       
         i 
        
       
         , 
        
       
         j 
        
       
         ) 
        
       
         − 
        
       
         > 
        
       
         ( 
        
       
         i 
        
       
         , 
        
       
         j 
        
       
         − 
        
       
         1 
        
       
         ) 
        
       
      
        (i,j)->(i,j-1) 
       
      
    (i,j)−>(i,j−1),边权为 
     
      
       
        
        
          v 
         
        
          j 
         
        
       
         − 
        
        
        
          v 
         
         
         
           j 
          
         
           − 
          
         
           1 
          
         
        
       
      
        v_{j}-v_{j-1} 
       
      
    vj−vj−1,可理解成是反悔边,反悔这次距离的添加
 每个代表点 
     
      
       
       
         ( 
        
       
         i 
        
       
         , 
        
       
         j 
        
       
         ) 
        
       
      
        (i,j) 
       
      
    (i,j)连向所有跟 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i距离为 
     
      
       
       
         j 
        
       
      
        j 
       
      
    j的具体点, 
     
      
       
       
         ( 
        
       
         i 
        
       
         , 
        
       
         j 
        
       
         ) 
        
       
         − 
        
       
         > 
        
       
         k 
        
       
         当 
        
       
         d 
        
       
         i 
        
       
         s 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         [ 
        
       
         k 
        
       
         ] 
        
       
         = 
        
       
         j 
        
       
      
        (i,j)->k 当dis[i][k]=j 
       
      
    (i,j)−>k当dis[i][k]=j,边权为无穷
 源点向所有代表点连边,边权为无穷, 
     
      
       
       
         S 
        
       
         − 
        
       
         > 
        
       
         ( 
        
       
         i 
        
       
         , 
        
       
         j 
        
       
         ) 
        
       
      
        S->(i,j) 
       
      
    S−>(i,j)
 所有具体点向汇点连边,边权为对应的 
     
      
       
        
        
          w 
         
        
          i 
         
        
       
      
        w_i 
       
      
    wi, 
     
      
       
       
         i 
        
       
         − 
        
       
         > 
        
       
         T 
        
       
      
        i->T 
       
      
    i−>T,因为一个点最坏情况就是直接修了它
 那么最小花费就是求最小割,直接网络流即可
 那么答案就是 
     
      
       
       
         n 
        
       
         ∗ 
        
        
        
          v 
         
        
          n 
         
        
       
         − 
        
       
         m 
        
       
         a 
        
       
         x 
        
       
         f 
        
       
         l 
        
       
         o 
        
       
         w 
        
       
      
        n*v_n-maxflow 
       
      
    n∗vn−maxflow
#include<bits/stdc++.h>
#define INF 0x7fffffff
using namespace std;
const int N = 205;
const int M = 4e5+5;
struct node {
	int to, next, w;
}e[M];
int n, m, k = 1, head[M], d[M];
int id[N][N], w[N], v[N];
int f[N][N];
int S, T, Maxflow, Num;
void build(int u, int v, int w) {
	e[++k] = (node) {v, head[u], w};
	head[u] = k;
	e[++k] = (node) {u, head[v], 0};
	head[v] = k;
}
queue<int> q;
bool bfs() {
	while(!q.empty()) q.pop();
	for(int i = S; i <= T; ++i) d[i] = 0;
	d[S] = 1;
	q.push(S);
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		for(int i = head[u]; i; i = e[i].next){
			int v = e[i].to;
			if(e[i].w && !d[v]) {
				d[v] = d[u] + 1;
				q.push(v);
				if(v == T) return 1;
			}
		}
	}
	return 0;
}
int dfs(int dep, int flow) {
	if(dep == T) {
		return flow;
	}
	int rest = flow, rp = 0;
	for(int i = head[dep]; i && rest; i = e[i].next) {
		int v = e[i].to;
		if(d[v] == d[dep] + 1 && e[i].w) {
			rp = dfs(v, min(e[i].w, rest));
			if(!rp) {
				d[v] = 0;
			}
			e[i].w -= rp;
			e[i ^ 1].w += rp;
			rest -= rp;
		}
	}
	return flow - rest;
}
void dinic() {
	int flow;
	while(bfs()) {
		while(flow = dfs(S, INF)) {
			Maxflow += flow;
		}
	}
}
 
int main() {
	scanf("%d", &n);
	S = 0;
	T = n*n+n+1;
	for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
	for (int i = 1; i <= n; i++) scanf("%d", &v[i]); 
	int U, V;
	memset(f, 0x3f, sizeof(f));
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &U, &V);
		if (U != V) f[U][V] = f[V][U] = 1;
	}
	for (int i = 1; i <= n; i++) build(i, T, w[i]);
	Num = n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) id[i][j] = ++Num;
    for (int i = 1; i <= n; i++) {
    	for (int j = 1; j <= n; j++) {
    		build(S, id[i][j], v[j]-v[j-1]);
    		build(id[i][j], id[i][j-1], INF); 
		}
	}    
	for (int i = 1; i <= n; i++) f[i][i] = 0;
	for (int k = 1; k <= n; k++)
	   for (int i = 1; i <= n; i++)
	       for (int j = 1; j <= n; j++) f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= n; j++) {
			build(id[i][f[i][j]+1], j, INF); 
		}
	dinic();
	printf("%d\n", n*v[n]-Maxflow);
	return 0;
}
 
J.Circular Billiard Table
签到题,我写的,题意是给一个入射角,求在圆内最少弹射多少次能回到原点
 入射角是 
     
      
       
        
        
          a 
         
        
          b 
         
        
       
      
        \frac{a}{b} 
       
      
    ba,那么假设有 
     
      
       
       
         x 
        
       
      
        x 
       
      
    x个转角,要弹 
     
      
       
       
         y 
        
       
      
        y 
       
      
    y圈
 显然有 
     
      
       
        
        
          a 
         
        
          b 
         
        
       
         ∗ 
        
       
         x 
        
       
         ∗ 
        
       
         2 
        
       
         = 
        
       
         360 
        
       
         ∗ 
        
       
         y 
        
       
      
        \frac{a}{b}*x*2=360*y 
       
      
    ba∗x∗2=360∗y
 构造 
     
      
       
       
         x 
        
       
         = 
        
       
         180 
        
       
         ∗ 
        
       
         b 
        
       
         , 
        
       
         y 
        
       
         = 
        
       
         a 
        
       
      
        x=180*b,y=a 
       
      
    x=180∗b,y=a然后除 
     
      
       
       
         g 
        
       
         c 
        
       
         d 
        
       
      
        gcd 
       
      
    gcd就可以了
#include<bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
 
ll gcd(ll a, ll b) {
	return (!b) ? a : gcd(b, a%b);
}
ll t, a, b;
 
int main() {
	scanf("%lld", &t);
	while (t--) {
		scanf("%lld%lld", &a, &b);
		long long A = 180*b, B = a;
		ll c = gcd(A, B);
		ll ans = A/c-1;
		printf("%lld\n", ans);
	}
	return 0;
} 
 
M.810975
题意是构造一个长度为 
     
      
       
       
         n 
        
       
      
        n 
       
      
    n的01串,其中有 
     
      
       
       
         m 
        
       
      
        m 
       
      
    m个1,最长连续1 等于 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k,问总方案数
 相当于是在0的 
     
      
       
       
         n 
        
       
         − 
        
       
         m 
        
       
         + 
        
       
         1 
        
       
      
        n-m+1 
       
      
    n−m+1个空隙里面插入1
 容斥一下,
 答案即为
  
     
      
       
       
         n 
        
       
         − 
        
       
         m 
        
       
         + 
        
       
         1 
        
       
      
        n-m+1 
       
      
    n−m+1个数中,满足 
     
      
       
       
         0 
        
       
         < 
        
       
         = 
        
        
        
          x 
         
        
          i 
         
        
       
         < 
        
       
         = 
        
       
         k 
        
       
      
        0<=x_i<=k 
       
      
    0<=xi<=k且 
     
      
       
       
         ( 
        
        
        
          ∑ 
         
         
         
           i 
          
         
           = 
          
         
           1 
          
         
         
         
           n 
          
         
           − 
          
         
           m 
          
         
           + 
          
         
           1 
          
         
        
        
        
          x 
         
        
          i 
         
        
       
         ) 
        
       
         = 
        
       
         m 
        
       
      
        (\sum_{i=1}^{n-m+1}x_i)=m 
       
      
    (∑i=1n−m+1xi)=m的方案数
 减去
  
     
      
       
       
         n 
        
       
         − 
        
       
         m 
        
       
         + 
        
       
         1 
        
       
      
        n-m+1 
       
      
    n−m+1个数,满足 
     
      
       
       
         0 
        
       
         < 
        
       
         = 
        
        
        
          x 
         
        
          i 
         
        
       
         < 
        
       
         = 
        
       
         k 
        
       
         − 
        
       
         1 
        
       
      
        0<=x_i<=k-1 
       
      
    0<=xi<=k−1且 
     
      
       
       
         ( 
        
        
        
          ∑ 
         
         
         
           i 
          
         
           = 
          
         
           1 
          
         
         
         
           n 
          
         
           − 
          
         
           m 
          
         
           + 
          
         
           1 
          
         
        
        
        
          x 
         
        
          i 
         
        
       
         ) 
        
       
         = 
        
       
         m 
        
       
      
        (\sum_{i=1}^{n-m+1}x_i)=m 
       
      
    (∑i=1n−m+1xi)=m的方案数
经典问题,队友直接秒了!
#include <bits/stdc++.h>
using namespace std;
#define mo 998244353
#define maxn 1000005
#define MAXN 1000005
#define ll long long
namespace Poly
{
    inline void Add(int &x,int y){x+=y;x-=(x>=mo?mo:0);}
    inline int add(int x,int y){x+=y;return x>=mo?x-mo:x;}
    int A[maxn],B[maxn],C[maxn],D[maxn],E[maxn],inv[maxn],R[maxn],len,sz,ny;
    inline int ksm(int x,int y=mo-2)
    {
        int a=1;if(y<0)y+=mo-1;
        while(y)
        {
            if(y&1)a=1ll*a*x%mo;
            x=1ll*x*x%mo;y>>=1;
        }return a;
    }
    inline void init(int n)
    {
        int i;inv[1]=1;
        for(i=2;i<=n;++i)inv[i]=1ll*(mo-mo/i)*inv[mo%i]%mo;
    }
    inline void Inte(int *a,int n)
    {
        for(int i=n-1;i>=1;--i)
            a[i]=1ll*a[i-1]*inv[i]%mo;
        a[0]=0;
    }
    inline void Deri(int *a,int n)
    {
        for(int i=1;i<n;++i)
            a[i-1]=1ll*a[i]*i%mo;
        a[n-1]=0;
    }
    inline void pre(int n)
    {
        len=1,sz=-1;while(len<n)len<<=1,++sz;ny=ksm(len);
        for(int i=1;i<len;++i)R[i]=(R[i>>1]>>1)|((i&1)<<sz);
    }
    inline void NTT(int *a,int fl)//fl==1?3:332748118;
    {
        int i,j,k,t,y,p,w,wn;
        for(i=1;i<len;++i)if(i<R[i])swap(a[i],a[R[i]]);
        for(i=2,p=1;i<=len;p=i,i<<=1)
            for(wn=ksm(fl,(mo-1)/i),j=0;j<len;j+=i)
                for(w=1,k=0;k<p;++k,w=1ll*w*wn%mo)
                {
                    t=a[j|k],y=1ll*w*a[j|k|p]%mo;
                    a[j|k]=add(t,y),a[j|k|p]=add(t,mo-y);
                }
    }
    inline void Mul(int *a,int *b,int *c,int nm,int n,int m)//a is the ans;
    {
        if(!n||!m){fill(a,a+nm,0);return ;}pre(n+m-1);
        for(int i=0;i<n;++i)A[i]=b[i];fill(A+n,A+len,0);
        for(int i=0;i<m;++i)B[i]=c[i];fill(B+m,B+len,0);
        NTT(A,3);NTT(B,3);
        for(int i=0;i<len;++i)A[i]=1ll*A[i]*B[i]%mo;
        NTT(A,332748118);
        for(int i=0;i<nm;++i)a[i]=1ll*A[i]*ny%mo;
    }
    inline void Inv(int *a,int *b,int n)//this n include zero position;
    {
        if(n==1)return a[0]=ksm(b[0]),void();
        int m=(n+1)>>1,i;Inv(a,b,m);pre(n<<1);
        for(i=0;i<m;++i)A[i]=a[i];fill(A+m,A+len,0);
        for(i=0;i<n;++i)B[i]=b[i];fill(B+n,B+len,0);
        NTT(A,3);NTT(B,3);
        for(i=0;i<len;++i)A[i]=1ll*A[i]*add(2,mo-1ll*A[i]*B[i]%mo)%mo;
        NTT(A,332748118);
        for(i=0;i<n;++i)a[i]=1ll*A[i]*ny%mo;
    }
    inline void Ln(int *a,int *b,int n)//a is the ans;b[0]need equal 1;
    {
        Inv(a,b,n);int i;pre(n<<1);
        for(i=0;i<n;++i)C[i]=b[i];Deri(C,n);
        for(i=0;i<n;++i)A[i]=a[i];fill(A+n,A+len,0);
        for(i=0;i<n;++i)B[i]=C[i];fill(B+n,B+len,0);
        NTT(A,3);NTT(B,3);
        for(i=0;i<len;++i)A[i]=1ll*A[i]*B[i]%mo;
        NTT(A,332748118);
        for(i=0;i<n;++i)a[i]=1ll*A[i]*ny%mo;
        Inte(a,n);
    }
    inline void Exp(int *a,int *b,int n)//a is the ans;
    {
        if(n==1)return a[0]=1,void();
        int m=(n+1)>>1,i;Exp(a,b,m);Ln(D,a,n);pre(n<<1);
        for(i=0;i<m;++i)A[i]=a[i];fill(A+m,A+len,0);
        for(i=0;i<n;++i)B[i]=add(b[i],mo-D[i]);++B[0];fill(B+n,B+len,0);
        NTT(A,3);NTT(B,3);
        for(i=0;i<len;++i)A[i]=1ll*A[i]*B[i]%mo;
        NTT(A,332748118);
        for(i=0;i<n;++i)a[i]=1ll*A[i]*ny%mo;
    }
    inline void Ksm(int *a,int *b,int n, ll k)//a b should be differented;a is the ans;b[0] need equal 1;
    {
        Ln(E,b,n);k%=mo;
        for(int i=0;i<n;++i)E[i]=1ll*E[i]*k%mo;
        Exp(a,E,n);
    }
}
int n, m, k;
int a[MAXN];
int b[MAXN];
int main()
{
    cin >> n >> m >> k;
    if(n == 0)
    {
        if(m == 0 && k == 0) { cout << 1 << "\n"; return 0; }
        else { cout << 0 << "\n"; return 0; }
    }
    if(n < m) { cout << 0 << "\n"; return 0;}
    if(m < k) { cout << 0 << "\n"; return 0; }
    if(m == 0)
    {
        if(k == 0) { cout << 1 << "\n"; return 0;}
        else { cout << 0 << "\n"; return 0;}
    }
    if(k == 0)
    {
        if(m == 0) { cout << 1 << "\n"; return 0;}
        else { cout << 0 << "\n"; return 0; }
    }
    n = n - m + 1;
    Poly::init(800000);
    memset(a, 0, sizeof a);
    for(int i = 0; i <= k; ++i) a[i] = 1;
    Poly::Ksm(b, a, m + 1, n);
    int ans = b[m];
    memset(a, 0, sizeof a);
    for(int i = 0; i < k; ++i) a[i] = 1;
    memset(b, 0, sizeof b);
    Poly::Ksm(b, a, m + 1, n);
    ans = (ans - b[m] + mo) % mo;
    cout << ans << "\n";
    return 0;
}
                


















