暑假牛客杭电
7.19 牛客二
C
签到,注意数据范围
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int n, m;
int main()
{
scanf("%d%d",&n,&m);
if(n % 2 == 1 && m % 2 == 1) puts("NO");
else puts("YES");
return 0;
}
D
签到
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int T;
bool cmp(int a1, int b1, int a2, int b2)
{
if(a1 > b1) swap(a1, b1);
if(a2 > b2) swap(a2, b2);
//printf("a1=%d b1=%d a2=%d b2=%d\n",a1,b1,a2,b2);
if(a1 == 2 && b1 == 8) return true;
else if(a2 == 2 && b2 == 8) return false;
else if(a1 == b1 && a2 == b2) return a1 > a2;
else if(a1 == b1) return true;
else if(a2 == b2) return false;
else {
if((a1+b1)%10 == (a2+b2) % 10) return b1 > b2;
else return (a1+b1)%10 > (a2+b2) % 10;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
int a1, b1, a2, b2;
scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
if(a1 > b1) swap(a1, b1);
if(a2 > b2) swap(a2, b2);
if(a1 == a2 && b1 == b2) puts("tie");
else {
bool flag = cmp(a1,b1,a2,b2);
if(flag) puts("first");
else puts("second");
}
}
return 0;
}
F 立体几何
空间集合,求两个球相交部分的体积
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
int T;
const double pi = acos(-1);
double x[4], y[4], z[4];
double k1, k2;
double dis(double x1, double y1, double z1, double x2, double y2, double z2)
{
double ans = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1));
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
for(int i = 0;i < 4;i++) {
scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
}
scanf("%lf%lf",&k1,&k2);
double xc1, yc1, zc1, xc2, yc2, zc2, r1, r2;
xc1 = (k1*k1*x[1]-x[0])/(k1*k1-1);
yc1 = (k1*k1*y[1]-y[0])/(k1*k1-1);
zc1 = (k1*k1*z[1]-z[0])/(k1*k1-1);
xc2 = (k2*k2*x[3]-x[2])/(k2*k2-1);
yc2 = (k2*k2*y[3]-y[2])/(k2*k2-1);
zc2 = (k2*k2*z[3]-z[2])/(k2*k2-1);
r1 = k1 * dis(x[1],y[1],z[1],x[0],y[0],z[0]) / (k1*k1-1);
r2 = k2 * dis(x[3],y[3],z[3],x[2],y[2],z[2]) / (k2*k2-1);
double rmax = max(r1,r2), rmin = min(r1,r2);
double res = 0;
double d = (dis(xc1,yc1,zc1,xc2,yc2,zc2));
if(d >= r1 + r2) res = 0;
else if(d + rmin <= rmax) res = (4.0/3)*pi*rmin*rmin*rmin;
else {
double co = (rmax*rmax+d*d-rmin*rmin) / (2.0*d*rmax);
double h = rmax * (1-co);
res += (1.0/3)*pi*(3.0*rmax-h)*h*h;
co = (rmin * rmin + d*d -rmax*rmax) / (2.0*d*rmin);
h = rmin*(1-co);
res += (1.0/3)*pi*(3.0*rmin-h)*h*h;
}
printf("%.10f\n",res);
}
return 0;
}
K 思维+构造
题意:给出单调栈中数列前几个元素的size,求可能的序列。
将b数组补全,数组中元素相邻不能跳跃递增,补全之后进行排序即可,直接进行模拟。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int MAXN = 1e6+50;
int n, k;
struct node{
int p, x;
}N[MAXN];
int a[MAXN];
bool vis[MAXN];
int b[MAXN];
vector<node>v;
bool cmp(node a, node b)
{
if(a.x == b.x) return a.p > b.p;
else return a.x < b.x;
}
int main()
{
scanf("%d%d",&n,&k);
while(k--)
{
int p, x;
scanf("%d%d",&p,&x);
v.push_back({p,x});
b[p] = x;
}
int tmp = 0;
bool flag = true;
for(int i = 1;i <= n;i++)
{
if(!b[i]) {
b[i] = ++tmp;
v.push_back({i,tmp});
}
else {
if(b[i] - b[i-1] > 1) {
flag = false;
break;
}
tmp = b[i];
}
}
sort(v.begin(),v.end(),cmp);
/*
for(int i = 0;i < v.size();i++)
{
printf("%d %d\n",v[i].p,v[i].x);
}
*/
int cnt = 1;
for(int i = 0;i < v.size();i++)
{
a[v[i].p] = cnt;
cnt++;
}
if(!flag) puts("-1");
else{
for(int i = 1;i <= n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
return 0;
}
🔺 I BFS
BFS搜索等学会了再补
7.20 杭电一
1001
打表找规律签到
大数据本地就没过 wa了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int T;
ll qpow(ll a, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * a;
n >>= 1;
a = a * a;
}
return res;
}
int main()
{
scanf("%d",&T);
//printf("%lld\n",log_2(1e12));
//printf("%lld\n",qpow(2,log_2(1e12)-1)-1);
while(T--)
{
ll n, res;
scanf("%lld",&n);
if(n == 1) res = 0;
else {
ll cnt = 0;
n--;
while(n)
{
n >>= 1;
cnt++;
}
res = qpow(2,cnt-1)-1;
}
printf("%lld\n",res);
}
return 0;
}
1009 并查集+贪心
排序后从小到大连边,并查集维护连通块个数。
注意边界(最多联通块个数 和 只有一个连通块)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5+50, M = 5e5+50;
int T;
int n, m, k;
int fa[N];
struct node{
int a, b, w;
}Node[M];
int find(int x)
{
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
bool cmp(node &a, node &b)
{
return a.w < b.w;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
int cnt = n;
for(int i = 1;i <= n;i++) fa[i] = i;
for(int i = 0;i < m;i++)
{
int a, b, w;
scanf("%d%d%d",&a,&b,&w);
Node[i].a = a;
Node[i].b = b;
Node[i].w = w;
}
sort(Node,Node+m,cmp);
/*
for(int i = 0;i < m;i++)
{
printf("a = %d b = %d w = %d\n",Node[i].a,Node[i].b,Node[i].w);
}
*/
int D = 0;
bool flag = true;
if(n == k) D = 0;
else{
for(int i = 0;i < m;)
{
D = Node[i].w;
int u = find(Node[i].a), v = find(Node[i].b);
//printf("%d %d\n",Node[i].a,Node[i].b);
if(u != v)
{
fa[u] = v;
cnt--;
}
//printf("cnt = %d\n",cnt);
i++;
while(Node[i].w == D && i < m) {
int u = find(Node[i].a), v = find(Node[i].b);
if(u != v) {
fa[u] = v;
cnt--;
}
i++;
//printf("cnt = %d\n",cnt);
}
if(cnt == k) break;
else if(cnt < k) {
flag = false;
break;
}
}
}
/*
for(int i = 1;i <= n;i++) printf("%d ",fa[i]);
printf("\n");
*/
if(!flag || cnt != k) puts("-1");
else printf("%d\n",D);
}
return 0;
}
1005 素数筛
签到 找规律+素数筛
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 10000010;
int primes[MAXN], cnt, T, n;
bool vis[MAXN];
int main()
{
scanf("%d",&T);
for(int i = 2;i <= MAXN;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;primes[j] * i <= MAXN;j++)
{
vis[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
while(T--)
{
scanf("%d",&n);
ll res = 0;
for(int i = 3;i <= n;i++) {
if(!vis[i]) res += 2*i;
else res += i;
}
printf("%lld\n",res);
}
return 0;
}
1008 单调栈
题意:求列非递减的最大子矩阵面积
若当前元素比其上一行大则记为1,否则为0.(第一行为0)。
利用单调栈(悬线法)答案
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
using namespace std;
int T, n, m;
const int MAXN = 2e3+50;
int a[MAXN][MAXN], b[MAXN][MAXN], h[MAXN], stk[MAXN];
int main()
{
scanf("%d",&T);
while(T--)
{
memset(stk,0,sizeof(stk));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
scanf("%d",&a[i][j]);
b[i][j] = 0;
if(i > 1 && a[i][j] >= a[i-1][j])
{
b[i][j] = 1;
}
}
}
int ans = 0;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
if(b[i][j] == 0) h[j] = 1;
else h[j]++;
}
h[m+1] = 0;
int tot = 0;
for(int j = 1;j <= m+1;j++)
{
while(tot && h[stk[tot]] > h[j])
{
//printf("j = %d stk[tot-1] = %d h[stk[tot]] = %d ans = %d\n",j,stk[tot-1],h[stk[tot]],(j-stk[tot-1]-1)*h[stk[tot]]);
ans = max(ans,(j-stk[tot-1]-1)*h[stk[tot]]);
tot--;
}
stk[++tot] = j;
}
}
printf("%d\n",ans);
}
return 0;
}
/*
2
4 4
2 2 2 2
1 3 1 1
2 4 0 1
3 5 2 1
4 4
3 3 3 3
2 4 2 2
3 5 1 2
4 6 0 3
*/
🔺 1006 字典树维护异或和
题意:给n个数,让最小区间异或和不小于k,若区间长度相等,输出最小的左端点。
思路:对数列做前缀异或,将题面转化为找两个距离最近的数,使得他们的异或值不小于k。
枚举靠右的那个数,同时维护字典树,字典树每个节点保存范围内最靠右的点的位置。根据k来询问对应的log个节点,从而更新答案。
效率:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
7.22 杭电二
1001
计算棱长 n-1 正方体中 所有等边三角形, 计算上下底面每一种边长的等边三角形数量,边平行于每个面。
答案 ∑ i = 1 n − 1 8 i 3 \sum^{n-1}_{i=1}8i^3 ∑i=1n−18i3
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
int T;
ll n;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%lld",&n);
n = n % MOD;
ll ans = 2 * n % MOD * n % MOD * (n-1) % MOD * (n-1) % MOD;
printf("%lld\n",ans);
}
return 0;
}
1012
查找字符串中是否含有相应子串
#include <cstdio>
#include <iostream>
using namespace std;
int T; string s,s0="114514" ;
int main()
{
cin>>T;
for(int t=1;t<=T;t++)
{
cin>>s;
if(s.find(s0)!=string::npos) // npos表示没有找到
cout<<"AAAAAA"<<endl;
else
cout<<"Abuchulaile"<<endl;
}
return 0;
}
1005
签到,每次可以将序列中当前字母插入序列之首或序列之尾。问字典序最小可能有几种方式。
答案是 2 序 列 开 头 相 同 字 母 数 − 1 2^{序列开头相同字母数-1} 2序列开头相同字母数−1,对后面字母只能插在特定位置
#include <cstdio>
#include <iostream>
#define mod 1000000007
using namespace std;
int T,l,maxl; string s; long long ans;
long long pow(int x);
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
cin>>T;
for(int t=1;t<=T;t++)
{
cin>>l>>s; maxl=0;
for(int i=1;i<l;i++)
{
if(s[i]==s[i-1])
maxl=i;
else
break;
}
ans=pow(maxl);
printf("%lld\n",ans);
}
return 0;
}
long long pow(int x)
{
if(x==0) return 1;
long long k=pow(x/2);
k=(k*k)%mod;
if(x&1) k=(k*2)%mod;
return k;
}
1008 背包DP
解法1
f [ i ] [ j ] f[i][j] f[i][j]表示第i个科目拿到k分的最短时间,跑一遍01背包。
m x [ i ] [ j ] mx[i][j] mx[i][j]表示第i个科目花费k天的最大分数。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示i天挂k科的最大分数
初始化 f [ i ] [ j ] = i n f , d p [ i ] [ j ] = − i n f , m x [ i ] [ j ] = 0 f[i][j]=inf, dp[i][j] = -inf, mx[i][j] = 0 f[i][j]=inf,dp[i][j]=−inf,mx[i][j]=0
状态转移
f[i][j] = f[i][j-gra]+day
01背包
mx[i][f[i][k]] = k
k = 1~100
dp[j][k] = max(dp[j][k],dp[j-l][k]+mx[i][l]) mx[i][l] >= 60
dp[j][k] = max(dp[j][k],dp[j-l][k-1]+mx[i][l]) mx[i][l] < 60
i = 1~n j = t~1 k = 0~p l = 1 − m i n ( f [ i ] [ 100 ] , j ) l = 1-min(f[i][100],j) l=1−min(f[i][100],j)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
const int M = 505;
struct node{
int gra, day;
};
map<string, int>mp;
int dp[1009][15], f[110][130], mx[55][1001];
int T, n, m, t, p;
vector<node>v[500];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
char op[20];
for(int i = 1;i <= n;i++)
{
cin >> op;
mp[op] = i;
}
scanf("%d",&m);
for(int i = 1;i <= m;i++)
{
int x, y;
cin >> op >> x >> y;
int id = mp[op];
v[id].push_back(node{x,y});
}
scanf("%d%d",&t,&p);
/*
for(int i = 1;i <= n;i++)
{
for(int j = 0;j < v[i].size();j++)
{
printf("i = %d day = %d gra = %d\n",i,v[i][j].day, v[i][j].gra);
}
}
*/
memset(f,0x3f,sizeof(f));
memset(dp,-0x3f,sizeof(dp));
memset(mx,0,sizeof(mx));
for(int i = 1;i <= n;i++)
{
f[i][0] = 0;
for(int j = 0;j < v[i].size();j++)
{
for(int k = 120;k >= v[i][j].gra;k--)
{
f[i][k] = min(f[i][k], f[i][k-v[i][j].gra] + v[i][j].day);
}
}
for(int k = 120; k >= 100; k--) f[i][k] = min(f[i][k],f[i][k+1]);
for(int k = 1;k <= 100;k++) if(f[i][k] <= 500) mx[i][f[i][k]] = max(mx[i][f[i][k]], k);
}
/*
for(int i = 1;i <= n;i++)
{
for(int k = 1;k <= 100;k++)
{
if(f[i][k] != 1061109567) printf("f[%d][%d] = %d ",i,k,f[i][k]);
}
printf("\n");
}
*/
dp[0][0] = 0;
for(int i = 1;i <= n;i++)
{
for(int j = t;j >= 1;j--)
{
for(int k = p; k > 0; k--) dp[j][k] = dp[j][k-1];
dp[j][0] = -1e9;
for(int k = 0;k <= p;k++)
{
for(int l = 1;l <= f[i][100] && l <= j;l++)
{
if(mx[i][l] >= 60) dp[j][k] = max(dp[j][k], dp[j-l][k] + mx[i][l]);
else if(k) dp[j][k] = max(dp[j][k], dp[j-l][k-1] + mx[i][l]);
}
}
}
dp[0][0] = -1e9;
}
int ans = -1;
for(int i = 1;i <= t;i++)
{
for(int j = 0;j <= p;j++)
{
ans = max(ans,dp[i][j]);
}
}
printf("%d\n",ans);
mp.clear();
for(int i = 1;i <= n;i++) v[i].clear();
}
return 0;
}
解法2 分组背包思想
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 55, S = 105, Time = 510, INF = 0x3f3f3f3f;
int g[N][S], f[N][Time][5];
int n, idx, m, t, p;
typedef pair<int, int> P;
unordered_map<string, int> mp;
vector<P> scores[N];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
mp.clear();
idx = 0;
memset(g, 0x3f, sizeof g);
memset(f, -0x3f, sizeof f);
scanf("%d", &n);
for(int i = 1; i <= n; i++) scores[i].clear();
for(int i = 1; i <= n; i++)
{
string s;
cin >> s;
mp[s] = ++idx;
}
scanf("%d", &m);
for(int i = 1; i <= m; i++)
{
string s;
int score, day;
cin >> s;
scanf("%d%d", &score, &day);
scores[mp[s]].push_back({score, day});
}
scanf("%d%d", &t, &p);
for(int id = 1; id <= n; id++)
{
g[id][0] = 0;
for(int i = 1, sz = scores[id].size(); i <= sz; i++)
{
int score = scores[id][i - 1].x, day = scores[id][i - 1].y;
for(int j = 100; j >= score; j--)
{
g[id][j] = min(g[id][j], g[id][j - score] + day);
}
}
}
f[0][0][0] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = t; j >= 0; j--)
{
for(int k = 0; k <= 100; k++)
{
int day = g[i][k];
for(int pp = p; pp >= 0; pp--)
{
int kk = k < 60 ;
if(pp - kk < 0 || j - day < 0) continue;
f[i][j][pp] = max(f[i][j][pp], f[i - 1][j - day][pp - kk] + k);
}
}
}
}
int ans = -INF;
for(int i = 0; i <= t; i++)
for(int pp = 0; pp <= p; pp++)
{
ans = max(ans, f[n][t][pp]);
}
if(ans <= -INF / 2) ans = -1;
printf("%d\n", ans);
}
}
7.24 牛客三
E
按照每一列找规律
s [ 2 ] = s [ 1 ] 3 s[2] = s[1]^3 s[2]=s[1]3
s [ i + 1 ] = s [ i ] ∗ b a s e 2 − s [ i − 1 ] s[i+1] = s[i] * base ^ 2 - s[i-1] s[i+1]=s[i]∗base2−s[i−1]
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
struct node{
ll i, cnt;
};
int T;
ll n;
ll ans[2000000], id;
void solve()
{
ans[0] = 1;
for(ll i = 2;i <= 1e6;i++)
{
__int128 tmp = i*i*i, lasttmp = i, llasttmp = i;
ans[++id] = tmp;
while(true)
{
lasttmp = tmp;
tmp = tmp * i * i - llasttmp;
if(tmp > 1e18) break;
ans[++id] = tmp;
llasttmp = lasttmp;
}
}
sort(ans,ans+1+id);
}
int main()
{
solve();
scanf("%d",&T);
while(T--)
{
scanf("%llu",&n);
ll res = upper_bound(ans,ans+id+1,n)-ans;
printf("%llu\n",res);
}
return 0;
}
//1000000000000000000
J
求边颜色相同的三角形
用总数减去边异色的三角形(考虑顶点)
a n s = ∑ w [ i ] ∗ ( n − 1 − w [ i ] ) ans = \sum w[i] * (n-1-w[i]) ans=∑w[i]∗(n−1−w[i])
C n 3 − a n s / 2 C_n^3 - ans / 2 Cn3−ans/2
#include <cstdio>
#include <bitset>
using namespace std;
unsigned z1,z2,z3,z4,b,u;
bitset<8010> edge[8010];
unsigned get()
{
b=((z1<<6)^z1)>>13;
z1=((z1&4294967294U)<<18)^b;
b=((z2<<2)^z2)>>27;
z2=((z2&4294967288U)<<2)^b;
b=((z3<<13)^z3)>>21;
z3=((z3&4294967280U)<<7)^b;
b=((z4<<3)^z4)>>12;
z4=((z4&4294967168U)<<13)^b;
return (z1^z2^z3^z4);
}
bool read()
{
while(!u) u=get();
bool res=u&1;
u>>=1; return res;
}
void srand(int x)
{
z1=x;
z2=(~x)^0x233333333U;
z3=x^0x1234598766U;
z4=(~x)+51;
u = 0;
}
int w[8010]={0}; long long ans=0;
int main()
{
int n,seed;
scanf("%d%d",&n,&seed);
srand(seed);
for(int i=0;i<n;i++)
w[i]=0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
edge[j][i]=edge[i][j]=read();
if(edge[i][j]==0)
++w[i],++w[j];
}
for(int i=0;i<n;i++)
ans+=w[i]*(n-1-w[i]);
printf("%lld",(long long)n*(n-1)*(n-2)/6-ans/2);
return 0;
}
B 最小生成树
题意:2*2的正方形染3个第四个格子自动染色,染每个格子都有本身的花费,求总共最小的花费。
所有格子被染成黑色的充要条件为:
将每一行和每一列作为一个顶点,对应格子的权重为当前行到当前列的连边,求其最小生成树。
原因为:每个各自表示每一行和每一列,需要最少填满n+m个格子才能全部染黑。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int n, m;
int a, b, c, d, p;
int A[25000001], id;
struct edge{
int u, v;
int w;
bool operator<(const edge &a)
{
return w < a.w;
}
}E[25000001];
int fa[10001];
int find(int x)
{
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
for(int i = 1;i <= n+m;i++) fa[i] = i;
A[0] = a;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
int &x = A[m *(i - 1) + j - 1];
A[m *(i - 1) + j] = ((ll)x * x * b + x * c + d) % p;
}
}
/*
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
printf("%d ",A[m*(i-1)+j]);
}
printf("\n");
}
*/
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
E[id++] = {i,j+n,A[m*(i-1)+j]};
}
}
sort(E,E+id);
ll ans = 0, cnt = 0;
for(int i = 0;i < id;i++)
{
//printf("%d %d %d\n",E[i].u,E[i].v,E[i].w);
if(cnt >= n+m-1) break;
int u = find(E[i].u), v = find(E[i].v);
if(u != v)
{
ans += E[i].w;
fa[u] = v;
cnt++;
}
}
printf("%lld\n",ans);
return 0;
}
7.26 牛客四
F 思维
每次可以删掉一条边或者一个子树
运用并查集,先将图删成一棵树再一次去掉子树
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 110;
int n, m;
int fa[MAXN], sz[MAXN];
int find(int x)
{
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) fa[i] = i;
int cnt = n;
while(m--)
{
int u, v;
scanf("%d%d",&u,&v);
int x = find(u), y = find(v);
if(x != y)
{
fa[x] = y;
cnt--;
}
else cnt++;
}
if(cnt % 2 == 0) puts("Bob");
else puts("Alice");
return 0;
}
I 树状数组求逆序对
答案为逆序对数 - 不相邻的从1枚举到n的逆序对
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 2e5+50;
int n, m;
int a[MAXN], c[MAXN], b[MAXN];
ll w[MAXN];
struct node{
int num, id;
bool operator<(const node &a)
{
return num < a.num;
}
}N[MAXN];
inline int lowbit(int x)
{
return x & (-x);
}
void change(int i, int k)
{
while(i <= n)
{
c[i] += k;
i += lowbit(i);
}
}
int sum(int x)
{
int ret = 0;
while(x)
{
ret += c[x];
x -= lowbit(x);
}
return ret;
}
int main()
{
scanf("%d",&n);
// 以下求区间逆序对O(nlogn)
for(int i = 1;i <= n;i++) {
scanf("%d",&a[i]);
N[i].id = i;
N[i].num = a[i];
}
sort(N+1,N+n+1);
for(int i = 1;i <= n;i++) b[N[i].id] = i;
ll ans = 0;
for(int i = n;i > 0;i--)
{
change(b[i],1);
ans += sum(b[i] - 1);
}
ll res = 0;
for(int i = 2;i <= n;)
{
if(N[i].id < N[i-1].id)
{
w[i] = 1;
i++;
}
i++;
}
for(int i = 1;i <= n;i++) res += w[i];
printf("%lld\n",ans-res);
return 0;
}
C 构造思维
构造最长公共子序列为特定值的情况
注意:序列长度可以无限大,且一个完整的子序列只要有三个字母
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1010;
int a, b, c, n;
char s1[MAXN], s2[MAXN], s3[MAXN], id;
int main()
{
scanf("%d%d%d%d",&a,&b,&c,&n);
int minn = min(c,min(a,b));
int i = 0;
for(;i < minn;i++)
{
s1[i] = 'a';
s2[i] = 'a';
s3[i] = 'a';
}
if(a == minn)
{
if(b+c-a > n) {
puts("NO");
return 0;
}
for(;i < b;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'b';
}
for(;i < b+c-a;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'c';
}
for(;i < n;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'd';
}
}
else if(b == minn)
{
if(c+a-b > n){
puts("NO");
return 0;
}
for(;i < a;i++)
{
s1[i] = 'b';
s2[i] = 'b';
s3[i] = 'c';
}
for(;i < c+a-b;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'c';
}
int id = 0;
for(;i < n;i++)
{
s1[i] = 'd';
s2[i] = 'b';
s3[i] = 'c';
}
}
else{
if(a+b-c > n) {
puts("NO");
return 0;
}
for(;i < a;i++)
{
s1[i] = 'b';
s2[i] = 'b';
s3[i] = 'c';
}
for(;i < a+b-c;i++)
{
s1[i] = 'b';
s2[i] = 'c';
s3[i] = 'c';
}
for(;i < n;i++)
{
s1[i] = 'b';
s2[i] = 'd';
s3[i] = 'c';
}
}
printf("%s\n",s1);
printf("%s\n",s2);
printf("%s\n",s3);
return 0;
}
J 二分求区间平均值的最大值
求两个数组子区间最大平均值之和
二分查找子区间平均值最大值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int n, m, x, y;
double a[MAXN], b[MAXN], tmp[MAXN], sa[MAXN], sb[MAXN];
// 二分查找子区间平均值最大值(二分mid,然后前缀和数组进行处理)
bool check(double a[], int len, int limit, double x, double sa[])
{
for(int i = 1;i <= len;i++) tmp[i] = a[i] - x;
sa[0] = 0;
for(int i = 1;i <= len;i++) sa[i] = sa[i-1] + tmp[i];
double ans = -1e6, res = 1e6;
for(int i = limit;i <= len;i++)
{
res = min(res, sa[i - limit]);
ans = max(ans, sa[i] - res);
}
if(ans >= 0) return true;
else return false;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&x,&y);
for(int i = 1;i <= n;i++) scanf("%lf",&a[i]);
for(int i = 1;i <= m;i++) scanf("%lf",&b[i]);
double r = 1e9, l = 0;
double ans = 0;
while(r - l > 1e-10)
{
double mid = (l+r) / 2;
if(check(a, n, x, mid, sa)) l = mid;
else r = mid;
}
ans += r;
r = 1e9, l = 0;
while(r - l > 1e-10)
{
double mid = (l+r) / 2;
if(check(b, m, y, mid, sb)) l = mid;
else r = mid;
}
ans += r;
printf("%.10f\n",ans);
return 0;
}
7.27 杭电三
1011
线段树中区间长度最多为k,问n个结点最多有几个区间
深搜,用unordered_map存n个结点的区间个数
偶数 mp[k] = 2*dfs(k / 2) + 1
奇数 mp[k] = dfs(k / 2) + dfs(k / 2 + 1) + 1
深搜+记忆化搜索
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef unsigned long long ll;
const int MAXN = 1e7+50;
int T;
ll k, n;
unordered_map<ll, ll>mp;
void init(ll n)
{
mp.clear();
ll base = 2;
for(ll i = n;i <= k;i *= 2)
{
mp[i] = base-1;
base *= 2;
}
}
ll dfs(ll k, ll n)
{
if(k <= n) return mp[n];
if(mp[k]) return mp[k];
if(k % 2 == 0) {
mp[k] = 2*dfs(k / 2, n) + 1;
return 2*dfs(k / 2, n) + 1;
}
else {
mp[k] = dfs(k / 2, n) + dfs(k / 2 + 1, n) + 1;
return dfs(k / 2, n) + dfs(k / 2 + 1, n) + 1;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%llu%llu",&k,&n);
init(n);
ll cnt = dfs(k, n);
printf("%llu\n",cnt);
}
return 0;
}
1007
简单前缀和问题(只有区间查询)
存储每个数前最近的1,维护区间和
r[rr] - r[max(fa[rr], ll)-1]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e6+50;
int T, n, q;
struct color{
int op, r0, g0, b0;
}Color[MAXN];
int fa[MAXN];
int r[MAXN], g[MAXN], b[MAXN];
void merge(int x)
{
if(Color[x].op == 1) fa[x] = x;
else {
fa[x] = fa[x-1];
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&q);
for(int i = 1;i <= n;i++) fa[i] = i;
for(int i = 1;i <= n;i++)
{
int op, x;
scanf("%d%X",&op,&x);
Color[i].op = op;
Color[i].b0 = x % 256;
Color[i].r0 = x / 256 / 256;
Color[i].g0 = x / 256 % 256;
merge(i);
r[i] = r[i-1] + Color[i].r0;
b[i] = b[i-1] + Color[i].b0;
g[i] = g[i-1] + Color[i].g0;
}
while(q--)
{
int ll, rr;
scanf("%d%d",&ll,&rr);
int ans1 = min(255,r[rr] - r[max(fa[rr], ll)-1]);
int ans2 = min(255,g[rr] - g[max(fa[rr], ll)-1]);
int ans3 = min(255,b[rr] - b[max(fa[rr], ll)-1]);
printf("%02X%02X%02X\n",ans1,ans2,ans3);
}
}
return 0;
}
7.29 杭电四
1001
判断+后面是不是0 && 开头必须是0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
string s;
int T;
int main()
{
scanf("%d",&T);
while(T--)
{
bool flag = true;
cin >> s;
if(s[0] != '0') {
flag = false;
}
for(int i = 0;i < s.size();i++)
{
if(s[i] == '+') {
if(s[i+1] != '0') {
flag = false;
break;
}
}
}
if(flag) puts("YES");
else puts("NO");
}
}
1009
输入字符 倒序判断(汉字可能不连通)
#include <cstdio>
#include <string>
#include<iostream>
#include<algorithm>
using namespace std;
int T; char c=1; bool in[110];
struct node{
int l, r;
bool operator<(const node &a)
{
return l < a.l;
}
}N[10];
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
for(int i=1;i<=100;i++)
in[i]=0;
for(int i=1;i<=30;i++)
{
for(int j=1;j<=100;j++)
{
c=getchar();
while(c!='.'&&c!='#') c=getchar();
if(c=='#') in[j]=1;
}
}
//for(int i = 1;i <= 100;i++) printf("%d%c",in[i],i % 10 == 0 ? '\n' : ' ');
printf("Case #%d:\n",t);
int cnt = 0, i;
for(i=100;i>=1 && cnt < 6;i--)
{
if(in[i]==0)
continue;
N[cnt].r = i;
while(in[i]&&i>=1) i--;
N[cnt].l = i+1;
cnt++;
}
while(in[i] == 0) i--;
N[cnt].r = i;
int j = 1;
while(in[j] == 0) j++;
N[cnt].l = j;
cnt++;
sort(N,N+cnt);
for(int i = 0;i < cnt;i++)
{
printf("%d %d\n",N[i].l, N[i].r);
}
}
return 0;
}
1002
dfs暴搜,以每个节点为根进行dfs,然后求出 w [ i ] [ j ] w[i][j] w[i][j]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 2030;
const int DA = 19560929;
const int mod1 = 1e9+7;
const int mod2 = 1e9+9;
int T, n, c[MAXN], w[MAXN][MAXN], head[MAXN], id, cnt[MAXN];
bool st[MAXN];
ll po1[MAXN], po2[MAXN];
void init()
{
po1[0] = 1, po2[0] = 1;
for(int i = 1;i <= 2010;i++)
{
po1[i] = po1[i-1] * DA % mod1;
po2[i] = po2[i-1] * DA % mod2;
}
}
struct edge{
int next, to;
}E[MAXN * 4];
inline void addedge(int u, int v)
{
E[id].to = v;
E[id].next = head[u];
head[u] = id++;
return;
}
void dfs(int u, int cur)
{
st[cur] = true;
for(int i = head[cur];i != -1;i = E[i].next)
{
int p = E[i].to;
if(!st[p])
{
if(cnt[c[p]]) w[u][p] = w[u][cur];
else w[u][p] = w[u][cur] + 1;
cnt[c[p]]++;
dfs(u, p);
cnt[c[p]]--;
}
}
}
int main()
{
init();
scanf("%d",&T);
while(T--)
{
memset(head,-1,sizeof(head));
memset(w,0,sizeof(w));
id = 0;
scanf("%d",&n);
for(int i = 2;i <= n;i++)
{
int x;
scanf("%d",&x);
addedge(i, x);
addedge(x, i);
}
for(int i = 1;i <= n;i++) scanf("%d",&c[i]);
for(int i = 1;i <= n;i++)
{
w[i][i] = 1;
for(int j = 1;j <= n;j++) st[j] = false;
for(int j = 1;j <= n;j++) cnt[j] = 0;
cnt[c[i]] = 1;
dfs(i,i);
}
/*
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
printf("%d ",w[i][j]);
}
printf("\n");
}
*/
for(int i = 1;i <= n;i++)
{
ll res1 = 0, res2 = 0;
for(int j = 1;j <= n;j++)
{
res1 = (res1 + w[i][j] * po1[j-1] % mod1) % mod1;
res2 = (res2 + w[i][j] * po2[j-1] % mod2) % mod2;
}
printf("%lld %lld\n",res1,res2);
}
}
return 0;
}
7.31 牛客五
H 构造
构造横竖斜着的连续三个矩阵中的数不相等
如下构造
1 1 0 0 1 1 0 0
0 0 1 1 0 0 1 1
1 1 0 0 1 1 0 0
0 0 1 1 0 0 1 1
#include <cstdio>
using namespace std;
int n,m;
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(j%4==1||j%4==2)
printf("%d",i%2);
else
printf("%d",1-i%2);
}
printf("\n");
}
return 0;
}
K
方法一:单调队列
题意:求满足:区间最大值-最小值>k的不同区间个数
枚举每一个左端点, 寻找最小右端点
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 50;
int n, m;
int a[MAXN], q1[MAXN], q2[MAXN];
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0;i < n;i++) scanf("%d",&a[i]);
while(m--)
{
int k;
scanf("%d",&k);
ll ans = 0;
int hh1 = 0, hh2 = 0, tt1 = -1, tt2 = -1;
q1[++tt1] = 0, q2[++tt2] = 0;
for(int i = 0, j = 0;i < n;i++)
{
while(hh1 <= tt1 && q1[hh1] < i) hh1++;
while(hh2 <= tt2 && q2[hh2] < i) hh2++;
while(j < n && a[q1[hh1]] - a[q2[hh2]] <= k)
{
j++;
while(hh1 <= tt1 && a[q1[tt1]] < a[j]) tt1--;
while(hh2 <= tt2 && a[q2[tt2]] > a[j]) tt2--;
q1[++tt1] = j; q2[++tt2] = j;
}
if(j < n) ans += n-j;
else break;
}
printf("%lld\n",ans);
}
return 0;
}
方法二:ST表+双指针
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+5, M = 22;
int n, m;
ll k;
int a[MAXN], maxx[MAXN][M], minn[MAXN][M], Log[MAXN];
void init()
{
for(int j = 0;j < M;j++)
{
for(int i = 1;i + (1 << j) - 1 <= n;i++)
{
if(j == 0) {
maxx[i][j] = a[i];
minn[i][j] = a[i];
}
else {
maxx[i][j] = max(maxx[i][j - 1], maxx[i + (1 << (j - 1))][j - 1]);
minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]);
}
}
}
}
void cal() // 求Log 否则cmath库中 log运行时间长
{
Log[1] = 0;
for(int i = 2;i <= MAXN;i++) Log[i] = Log[i/2]+1;
}
bool check(int l, int r, ll k)
{
int lo = Log[r-l+1];
int maxn = max(maxx[l][lo], maxx[r - (1 << lo) + 1][lo]);
int minx = min(minn[l][lo], minn[r - (1 << lo) + 1][lo]);
if(maxn - minx > k) return true;
else return false;
}
int main()
{
cal();
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
init();
while(m--)
{
scanf("%lld",&k);
ll ans = 0; // 注意每次初始化
for(int i = 1, j = 1;i <= n;i++)
{
while(!check(i, j, k) && j <= n && i <= j) j++;
if(j <= n) ans = ans+n-j+1;
}
printf("%lld\n", ans);
}
return 0;
}
B 概率
题意:开每一个箱子的代价为 w i w_i wi,询问一次剩下有几个黑球代价为c,每个盒子都有0.5的概率白 or 黑,且相互独立,问数学期望最小为多少。
不再开箱的条件为剩下球的颜色相同。
r e s = ∑ i = 1 n ( 1 2 ) n − i ( c + ∑ w i ) res = \sum_{i = 1}^{n}(\frac{1}{2})^{n-i}(c+\sum w_i) res=∑i=1n(21)n−i(c+∑wi)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M = 1e5+50;
double w[M], sum[M];
double c;
double poww[M];
int n;
void init()
{
poww[0] = 1;
for(int i = 1;i <= 1e5+5;i++) poww[i] = poww[i-1] * 0.5;
return;
}
bool cmp(double a, double b)
{
return a > b;
}
int main()
{
init();
scanf("%d%lf",&n,&c);
for(int i = 1;i <= n;i++) scanf("%lf",&w[i]);
sort(w+1,w+1+n);
sum[0] += c;
for(int i = 1;i <= n;i++) sum[i] = sum[i-1] + w[i];
double res = 0, las = 1;
res += sum[0] * poww[n-1];
for(int i = 1;i <= n-1;i++)
{
res += sum[i] * poww[n-i];
// printf("%.10f\n",res);
}
printf("%.10f\n",min(res, sum[n]-c));
return 0;
}
D 计数DP
- 在$ [1,i - 1]$区间里找一个公共子序列
- 在i 这个点 A [ i ] < B [ i ] A[i] < B[i] A[i]<B[i]
- i i i 后面的可以任意取。
计数时: d p [ i ] [ j ] dp[i][j] dp[i][j]表示 a [ 1 − i ] , b [ 1 − j ] a[1-i],b[1-j] a[1−i],b[1−j]的公共子序列个数(包含空串)
dp[i][j] = dp[i][j-1]+dp[i-1][j]; if(a[i] == b[j])
dp[i][j] = dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1] if(a[i] != b[j]) 不加1是因为本身dp计数就有空串
后面根据组合数,后 n − i n-i n−i和 m − j m-j m−j个中任取 i i i个
KaTeX parse error: Undefined control sequence: \C at position 26: …^{min(n-i,m-j)}\̲C̲_{n-i}^{k}\C^{k…
对于大数的处理,先定义大数的add, sub, mul避免出错
预处理逆元
方法1(快速幂)
int qmi(int a, int b, int mod)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
void init()
{
fact[0] = 1, infact[0] = 1;
for(int i = 1;i <= 2*5005;i++) {
fact[i] = mul(fact[i-1], i);
infact[i] = mul(infact[i-1], qmi(i, MOD-2, MOD));
}
return;
}
ll Cal(int n, int m)
{
if(n == 0 && m == 0) return 1;
return mul(mul(fact[n], infact[m]), infact[n-m]);
}
方法二(欧几里得)
ll exgcd(ll a, ll b , ll &x , ll &y){
if(b){
int r = exgcd(b , a % b , y , x);
y -= x * (a / b);
return r;
}
else{
x = 1 , y = 0;
return a;
}
}
ll inv(ll x){
ll a , b , c, d;
a = x , b = mod;
exgcd(a,b,c,d);
c %= mod;
return c < 0 ? mod + c : c;
}
ll dp[N][N];
ll F[2 * N];
ll invF[2 * N];
void pre(){
F[0] = 1;
for(int i=1; i < 2 * N ; i++){
F[i] = Mul(F[i-1] , i);
}
for(int i = 0; i < 2 * N; i++){
invF[i] = inv(F[i]);
}
}
ll C(ll n , ll x){
if(n == 0 && x == 0)return 1;
return F[n] * invF[x] % mod * invF[n - x] % mod;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int M = 5050;
char a[M], b[M];
ll fact[2*M], infact[2*M];
ll dp[M][M];
const int MOD = 1e9+7;
ll add(ll a, ll b)
{
return a + b >= MOD ? a + b - MOD : a + b;
}
ll sub(ll a, ll b)
{
return a - b >= 0 ? a - b : a - b + MOD;
}
ll mul(ll a, ll b)
{
return a * b % MOD;
}
int qmi(int a, int b, int mod)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
void init()
{
fact[0] = 1, infact[0] = 1;
for(int i = 1;i <= 2*5005;i++) {
fact[i] = mul(fact[i-1], i);
infact[i] = mul(infact[i-1], qmi(i, MOD-2, MOD));
}
return;
}
ll Cal(int n, int m)
{
if(n == 0 && m == 0) return 1;
return mul(mul(fact[n], infact[m]), infact[n-m]);
}
int main()
{
init();
cin >> a+1 >> b+1;
int n = strlen(a+1), m = strlen(b+1);
dp[0][0] = 1;
for(int i = 1;i <= n;i++) dp[i][0] = 1;
for(int i = 1;i <= m;i++) dp[0][i] = 1;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
dp[i][j] = sub(add(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]);
if(a[i] == b[j]) dp[i][j] = add(dp[i][j], dp[i-1][j-1]);
}
}
/*
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++) printf("%d ",dp[i][j]);
printf("\n");
}
*/
ll ans = 0, res = 0;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
int x = n-i, y = m-j;
if(a[i] < b[j]) {
res = mul(dp[i-1][j-1], Cal(x+y, x));
ans = add(ans, res);
}
}
}
printf("%lld\n", ans);
return 0;
}
8.2 牛客六
I
题意:找区间的交为给定区间的并
每个区间必须包括所有区间,所以取所有区间左端点和离它最远区间右端点
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1010;
int T, n, m;
int cf[MAXN * 2], cnt[MAXN];
struct node{
int l, r;
bool operator<(node &a)
{
return l < a.l;
}
}N[MAXN];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(cf, 0, sizeof(cf));
memset(cnt, 0, sizeof(cnt));
for(int i = 0;i < m;i++)
{
int l, r;
scanf("%d%d",&l, &r);
if(l <= r) {
cf[r+1]--;
cf[l]++;
}
else {
cf[r+n+1]--;
cf[l]++;
}
}
for(int i = 1;i <= 2 * n;i++)
{
cnt[i] = cnt[i-1] + cf[i];
}
for(int i = 1;i <= n;i++) cnt[i] += cnt[i+n];
/*
for(int i = 1;i <= n;i++) printf("%d ", cnt[i]);
printf("\n");
*/
int id = 0;
for(int i = 1;i <= n;)
{
while(!cnt[i]) i++;
if(i > n) break;
N[id].l = i;
while(cnt[i]) i++;
N[id].r = min(n, i-1);
id++;
}
/*
printf("\n");
for(int i = 0;i < id;i++) printf("%d %d\n", N[i].l, N[i].r);
printf("\n");
*/
sort(N, N+id);
printf("%d\n", id);
printf("%d %d\n", N[0].l, N[id-1].r);
for(int i = 1;i < id;i++)
{
printf("%d %d\n", N[i].l, N[i-1].r);
}
}
return 0;
}
/*
2
7 3
2 4
6 7
7 1
7 2
5 1
7 2
*/
F
答案为 m a x ( m a x ( a i ) , 平 均 数 ) max(max(a_i), 平均数) max(max(ai),平均数)
每次直接模拟,超过时间换一个锅
#include <cstdio>
using namespace std;
int n,m,num;
long long cnt,a[100010],sum,ans=0,maxx;
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum+=a[i];
if(a[i]>maxx) maxx=a[i];
}
ans=sum/m;
if(ans*m<sum) ++ans;
num=1; cnt=0;
if(ans<maxx)
ans=maxx;
for(int i=1;i<=n;i++)
{
if(a[i]+cnt<=ans)
printf("1 %d %lld %lld\n",num,cnt,cnt+a[i]),cnt+=a[i];
else
{
printf("2 %d %d %lld %d %lld %lld\n",num+1,0,a[i]-(ans-cnt),num,cnt,ans);
++num;
cnt=a[i]-(ans-cnt);
}
if(cnt==ans) ++num,cnt=0;
}
return 0;
}
8.9 牛客八
D
a + b = a & b + a | b
K
8.10 杭电七
🔺1007(拓扑排序)
题意: f n ( x ) = f ( f n − 1 ( x ) ) f 1 ( x ) = f ( x ) x , f ( x ) ∈ [ 1 , n ] f_n(x) = f(f_{n-1}(x)) \quad f_1(x) = f(x) \qquad x,f(x) ∈[1,n] fn(x)=f(fn−1(x))f1(x)=f(x)x,f(x)∈[1,n]
问 g ( x ) = lim m − > ∞ 1 m ∑ i = 1 m f i ( x ) 对 x ∈ [ 1 , n ] 是 否 相 同 g(x) = \lim_{m -> ∞} \frac{1}{m}\sum^m_{i=1}f_i(x) \qquad 对x ∈[1,n]是否相同 g(x)=limm−>∞m1∑i=1mfi(x)对x∈[1,n]是否相同
转化题意: i i i向 a [ i ] a[i] a[i]连边,问每一个环均值是否相等(进入环的那部分不算在内)
思路:拓扑排序搞掉进入环前的一部分,然后判断
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int T, n;
int a[MAXN], d[MAXN];
queue<int>q;
int main()
{
cin >> T;
while(T--)
{
while(!q.empty()) q.pop();
memset(d, 0, sizeof(d));
cin >> n;
for(int i = 1;i <= n;i++) {
scanf("%d", &a[i]);
d[a[i]]++;
}
// 处理进入环之前的无关节点
for(int i = 1;i <= n;i++){
if(d[i] == 0) q.push(i);
}
while(!q.empty())
{
int u = q.front();
q.pop();
d[a[u]]--;
if(d[a[u]] == 0) q.push(a[u]);
}
bool flag = true;
ll sum1 = -1, cnt1 = -1;
for(int i = 1;i <= n;i++)
{
if(d[i] == 0) continue;
ll sum2 = 0, cnt2 = 0;
for( ; d[i]; i = a[i])
{
d[i] = 0;
sum2 += i;
cnt2++;
}
if(sum1 == -1 && cnt1 == -1) {
sum1 = sum2;
cnt1 = cnt2;
}
else {
if(sum1 * cnt2 != sum2 * cnt1) {
flag = false;
break;
}
}
}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}
1003
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int MAXN = 10010;
int T, k;
double x, y, x_1, y_1, x_2, y_2, h, w;
double poww[MAXN];
void init()
{
poww[0] = 1.0;
for(int i = 1;i <= 10001;i++) poww[i] = poww[i-1] * 0.5;
}
int main()
{
init();
scanf("%d", &T);
while(T--)
{
cin >> k;
scanf("%lf%lf%lf%lf%lf%lf", &x, &y, &x_1, &y_1, &x_2, &y_2);
h = y - y_1;
w = abs(x_2 - x_1);
double ans;
if(k == 2) ans = w * h / 2;
else {
ans = (2 * (k - 3) + poww[k] * 6 + 1) * w * h;
}
printf("%.3f\n", ans);
}
return 0;
}
1010
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while(T --) {
double a, b;
scanf("%lf%lf", &a, &b);
if(a <= b) printf("N0 M0R3 BL4CK 1CE TEA!\n");
else printf("ENJ0Y YOURS3LF!\n");
}
return 0;
}
🔺 1005(线性dp)
题解思路正确,递推公式有误
f
[
i
]
=
f
[
m
i
d
−
2
]
+
f
[
l
e
n
−
m
i
d
−
1
]
+
1
f
[
1
]
=
f
[
2
]
=
0
g
[
i
]
=
g
[
i
−
2
]
+
1
g
[
1
]
=
0
h
[
i
]
=
1
n
∑
i
=
1
n
g
(
i
−
2
)
+
g
(
n
−
i
−
1
)
+
1
=
1
+
2
n
∑
i
=
1
n
−
2
g
(
i
)
f[i] = f[mid-2]+f[len-mid-1]+1 \\ f[1] = f[2] = 0 \\ g[i] = g[i-2]+1 \\ g[1] = 0 \\ h[i] = \frac{1}{n} \sum_{i=1}^{n}g(i-2)+g(n-i-1)+1 = 1 + \frac{2}{n}\sum_{i=1}^{n-2}g(i)
f[i]=f[mid−2]+f[len−mid−1]+1f[1]=f[2]=0g[i]=g[i−2]+1g[1]=0h[i]=n1i=1∑ng(i−2)+g(n−i−1)+1=1+n2i=1∑n−2g(i)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
const int MAXN = 1e6+50;
int T, n;
int num[MAXN][2], sum[MAXN];
int solve(int len, int type)
{
if(len <= 0) return 0;
if(num[len][type]) return num[len][type];
if(type == 0) {
return num[len][type] = solve(len - 2, 1) + 1; // g[i] = g[i-2]+1
}
else {
int mid = (len + 1) / 2;
return num[len][type] = solve(mid-2, 1) + solve(len-1-mid, 1) + 1;// f[i] = f[mid-2]+f[len-mid-1]+1
}
}
void init()
{
for(int i = 1;i <= 1000000;i++) solve(i, 0);
for(int i = 1;i <= 1000000;i++) sum[i] = (sum[i-1] + num[i][0]) % MOD;
}
int qmi(int a, int b, int mod)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll) res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
int main()
{
init();
cin >> T;
while(T--)
{
cin >> n;
int ans = (qmi(n, MOD-2, MOD) * (ll)(n + 2*sum[n-2]) % MOD + MOD) % MOD;
printf("%d\n", ans);
}
return 0;
}
🔺 1012(统计数量)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zx9xy4Of-1629254178298)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210811115024176.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4k4GO1aN-1629254178307)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210811115113170.png)]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
const int mod = 998244353;
int T, n;
char s[MAXN];
vector<int> a[28];
ll solve(vector<int> vec)
{
vec.push_back(n + 1);
ll ans = 0, per = 0, dif = 0;
for(int i = 1;i < vec.size();i++)
{
ans = (ans + per * (vec[i] - vec[i - 1])) % mod;
dif = (dif + 2 * vec[i - 1] + vec[i] - vec[i - 1]) % mod; // 差分
per = (per + dif) % mod; // 差分
}
return ans;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%s", s+1);
n = strlen(s+1);
for(int i = 0;i < 26;i++) a[i].clear();
for(int i = 0;i < 26;i++) a[i].push_back(0);
for(int i = 1;i <= n;i++)
{
a[s[i] - 'a'].push_back(i);
}
ll ans = 0;
for(int i = 0;i < 26;i++) ans = (ans + solve(a[i])) % mod;
printf("%lld\n", ans);
}
return 0;
}
🔺1004(生成函数)
对每个箱子分别求其生成函数
奇数
1 : 1 + x 1 + x 2 + ⋯ + = 1 1 − x 1: 1+x^1+x^2+\dots+=\frac{1}{1-x} 1:1+x1+x2+⋯+=1−x1
3 : 1 + x 2 + x 4 + ⋯ + = 1 1 − x 2 3:1+x^2+x^4+\dots+=\frac{1}{1-x^2} 3:1+x2+x4+⋯+=1−x21
2 t − 1 : 1 + x t + ⋯ + = 1 1 − x t 2t-1:1+x^t+\dots+=\frac{1}{1-x^t} 2t−1:1+xt+⋯+=1−xt1
偶数:
2 : 1 + x = 1 − x 2 1 − x 2:1+x = \frac{1-x^2}{1-x} 2:1+x=1−x1−x2
4 : 1 + x + x 2 = 1 − x 3 1 − x 4:1+x+x^2=\frac{1-x^3}{1-x} 4:1+x+x2=1−x1−x3
2 t : 1 + x + ⋯ + x t = 1 − x t 1 − x 2t:1+x+\dots+x^t=\frac{1-x^t}{1-x} 2t:1+x+⋯+xt=1−x1−xt
生成函数 f ( x ) = 1 − x n + 1 ( 1 − x ) n + 1 = 1 ( 1 − x ) n + 1 − x n + 1 ( 1 − x ) n + 1 f(x)=\frac{1-x^{n+1}}{(1-x)^{n+1}} = \frac{1}{(1-x)^{n+1}}-\frac{x^{n+1}}{(1-x)^{n+1}} f(x)=(1−x)n+11−xn+1=(1−x)n+11−(1−x)n+1xn+1
问 x m x^m xm的系数
KaTeX parse error: Undefined control sequence: \C at position 5: ans=\̲C̲_{m+n}^m - \C _…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 2e6+50;
const int mod = 1e9+7;
ll T, n, m;
ll fact[MAXN], infact[MAXN];
ll qmi(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
void init()
{
fact[1] = 1, infact[1] = 1;
for(int i = 2;i <= MAXN - 20;i++)
{
fact[i] = fact[i - 1] * i % mod;
infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
}
}
ll cal(ll m, ll n)
{
if(m < n) return 0; // 注意这个
ll ans = fact[m] * infact[n] % mod * infact[m - n] % mod;
return ans;
}
int main()
{
init();
cin >> T;
while(T--)
{
cin >> n >> m;
ll ans = ((cal(m+n, m) - cal(m-1, n)) % mod + mod) % mod;
printf("%lld\n", ans);
}
return 0;
}
8.12 杭电八
1006(素数筛筛约数个数+nim博弈)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e7+50;
int T, n;
bool vis[MAXN];
int primes[MAXN], f[MAXN], a[MAXN], cnt; // f[i]表示i的质因子个数
void get_primes(int n)
{
for(int i = 2;i <= n - 20;i++)
{
if(!vis[i])
{
primes[cnt++] = i;
f[i] = 1;
}
for(int j = 0;(ll)i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
f[i * primes[j]] = f[i] + 1;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
get_primes(MAXN - 20);
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
int ans = 0;
for(int i = 1;i <= n;i++)
{
scanf("%d", &a[i]);
if(a[i] == 1) a[i] = 0;
ans ^= f[a[i]]; // nim博弈
}
if(ans) puts("Alice");
else puts("Bob");
}
return 0;
}
1003(Prim)
最小生成树中的最大边
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5050;
int T, n;
bool vis[MAXN];
ll d[MAXN];
struct node{
ll x, y;
}N[MAXN];
ll prim()
{
ll res = 0;
for(int i = 0;i < n;i++)
{
int t = -1;
for(int j = 1;j <= n;j++)
{
if(!vis[j] && (t == -1 || d[t] > d[j])) t = j;
}
if(i) res = max(res, d[t]);
vis[t] = true;
for(int j = 1;j <= n;j++)
d[j] = min(d[j], (N[j].x - N[t].x) * (N[j].x - N[t].x) + (N[j].y - N[t].y) * (N[j].y - N[t].y));
}
return res;
}
int main()
{
scanf("%d", &T);
while(T--)
{
memset(vis, false, sizeof(vis));
memset(d, 0x3f, sizeof(d));
scanf("%d", &n);
for(int i = 1;i <= n;i++)
{
ll x, y;
scanf("%lld%lld", &x, &y);
N[i] = {x, y};
}
ll ans = prim();
printf("%lld\n", ans);
}
return 0;
}
8.12 杭电八
1006(素数筛筛约数个数+nim博弈)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e7+50;
int T, n;
bool vis[MAXN];
int primes[MAXN], f[MAXN], a[MAXN], cnt; // f[i]表示i的质因子个数
void get_primes(int n)
{
for(int i = 2;i <= n - 20;i++)
{
if(!vis[i])
{
primes[cnt++] = i;
f[i] = 1;
}
for(int j = 0;(ll)i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
f[i * primes[j]] = f[i] + 1;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
get_primes(MAXN - 20);
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
int ans = 0;
for(int i = 1;i <= n;i++)
{
scanf("%d", &a[i]);
if(a[i] == 1) a[i] = 0;
ans ^= f[a[i]]; // nim博弈
}
if(ans) puts("Alice");
else puts("Bob");
}
return 0;
}
1003(Prim)
最小生成树中的最大边
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5050;
int T, n;
bool vis[MAXN];
ll d[MAXN];
struct node{
ll x, y;
}N[MAXN];
ll prim()
{
ll res = 0;
for(int i = 0;i < n;i++)
{
int t = -1;
for(int j = 1;j <= n;j++)
{
if(!vis[j] && (t == -1 || d[t] > d[j])) t = j;
}
if(i) res = max(res, d[t]);
vis[t] = true;
for(int j = 1;j <= n;j++)
d[j] = min(d[j], (N[j].x - N[t].x) * (N[j].x - N[t].x) + (N[j].y - N[t].y) * (N[j].y - N[t].y));
}
return res;
}
int main()
{
scanf("%d", &T);
while(T--)
{
memset(vis, false, sizeof(vis));
memset(d, 0x3f, sizeof(d));
scanf("%d", &n);
for(int i = 1;i <= n;i++)
{
ll x, y;
scanf("%lld%lld", &x, &y);
N[i] = {x, y};
}
ll ans = prim();
printf("%lld\n", ans);
}
return 0;
}