难度不分先后
1007 | foreverlasting and fried-chicken |
我发图吧这个题赛中告诉队友做法了这里就不重复了
然后你就会发现上面直接搜点就会tle
想优化吧,用bitset
如果 x点能到达得点和y点相同,那么就可以做为中间的点,
然后贡献就是代码里面了 如果a能到达b,就减去这条边,我画个图吧
#include
using namespace std;
const int N = 1010,mod=1e9+7;
typedef pair PII;
#define int long long
typedef long long LL;
int n,m,k;
int fact[N],infact[N];
bitset g[N];
int qmi(int a, int k, int p) // 求a^k mod p
{
int res = 1 % p;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
void init(){
fact[0]=infact[0]=1;
for(int i=1;ia) return 0;
return (LL)fact[a] * infact[b] % mod * infact[a - b] % mod;
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++) g[i].reset();
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
g[a].set(b),g[b].set(a);
}
int res=0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
auto tmp=g[i]&g[j];
int cnt=tmp.count();
if(tmp.count()>=4)
{
int a=g[i].count(),b=g[j].count();
if(g[i][j]) a--,b--;
if(a>=6){
res+=C(cnt,4)*C(a-4,2);
res%=mod;
}
if(b>=6){
res+=C(cnt,4)*C(b-4,2);
res%=mod;
}
}
}
}
cout<>t;
while(t--) solve();
}
1002 | Binary Number |
首先手玩题
画画图找一下规律?
比如 1001001 让他变成全1
翻转区间[2,6] 再翻转[4,4]可以变成1
但是可以发现直接翻转 [2,3] [5.6]等价且实现简单
然后特判 k=1和n=1得情况这种情况很坑
#include
using namespace std;
const int N = 3e6+10;
typedef pair PII;
#define int long long
long long n,m,k;
int a[N];
void solve()
{
cin>>n>>k;
int r=0,l=0x3f3f3f3f;
string s;cin>>s;
if(n==1)
{
int x=k%2;
int res=s[0]-'0';
if(x&1) res^=1;
cout<>t;
while(t--) solve();
}
1009 | String Problem |
因为选的每个字符串只有一个字母,所以直接全选,并且选的连续长度越长越好
我直接双指针了
#include
using namespace std;
const int N = 3e6+10;
typedef pair PII;
#define int long long
int n,m,k;
int a[N];
void solve()
{
string s;
cin>>s;n=s.size();
vector cnt(30,0);
int res=0;
for(int i=0;i1)
{
i=j-1;
}
}
cout<>t;
while(t--) solve();
}
1010 | Klee likes making friends |
额应该很明显的dp?
然后怎么想呢我这边建议是画图想
首先看范围n=2e4 ,m=20很明显叫我循环n*m,
我直接定义了状态前i个人满足连续m个至少有两个好朋友,且必选第i个人,和前面第二个人的距离是j(因为你要至少两个人,所以你状态要可以表达出两个人的位置)
考虑初始化,直接初始化第一个m的连续区间的数,就是两个人的值了
状态怎么转移呢,
f[i][j]=a[i]+sum[i-j][m-j]; (j是枚举和当前i的距离,所以他的位置就是i-j了)
i-j到i中间是没数的因为他们表示最后一个人位置是i,倒数第二个人位置是i-j
所以第三个人位置是i-m到i-j-1,下面的图你可以看出来,不算7的话你要保证6结尾的连续m合法
所以第三个人一定在倒数第二个人人中我画的那里,他们的距离可以是1,2,3...,m-j
所以方程就可以列出来了,那么考虑优化
我们只要求第三个人的距离是1,2,3...,m-j,所以求他们的最小值就行,我们直接求前缀最小值即可,然后交了,可对但是会mle,因为第一维是n=2e4
下面的第一个代码就是我说的mle的代码
第二个代码是优化后的代码
考虑优化空间,我们只需要知道前面m个人的信息就好了,我们直接用取模方式优化空间即可,其他没啥变化
#include
using namespace std;
const int N = 2e4+10;
typedef pair PII;
int n,m,k;
int a[N];
int f[N][2010];
int sum[N][2010];
void solve()
{
cin>>n>>m;
int res=2e9;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++){
sum[i][0]=2e9;
for(int j=1;j<=m;j++){
if(i-j<=0) f[i][j]=2e9;
else f[i][j]=a[i]+a[i-j];
sum[i][j]=min(sum[i][j-1],f[i][j]);
}
}
for(int i=m+1;i<=n;i++)
{
sum[i][0]=2e9;
for(int j=1;j<=m;j++){
f[i][j]=a[i]+sum[i-j][m-j];
sum[i][j]=min(sum[i][j-1],f[i][j]);
}
}
for(int i=1;i<=m;++i) res=min(res,sum[n+1-i][m-i]);
cout<>t;
while(t--) solve();
}
#include
using namespace std;
const int N = 2e4+10;
typedef pair PII;
int n,m,k;
int a[N];
int f[2010][2010];
int sum[2010][2010];
int tdp[2010],tsum[2010];
void solve()
{
cin>>n>>m;
int res=2e18;
for(int i=0;i>a[i];
for(int i=0;i>t;
while(t--) solve();
}
1004 | Card Game |
这里写的歪解,看范围n=1e9,基本是个结论题,直接看答案...
然后就可以直接得出结论等于2^(n-1)-1
问就是猜的,补题也不准备想....
#include
using namespace std;
const int N = 2e4+10,mod=998244353;
#define int long long
typedef pair PII;
typedef long long LL;
int n,m,k;
int a[N];
int qmi(int a, int k, int p) // 求a^k mod p
{
int res = 1 % p;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
void solve()
{
cin>>n;
cout<<(((qmi(2,n-1,mod)-1)%mod)+mod)%mod<<"\n";
}
// 2 3 1 3 3
signed main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
int t=1;
cin>>t;
while(t--) solve();
}
1011 | SPY finding NPY |
首先k=0的时候,概率是1/n,每个人是最大值的概率是1/n
把每个不同k分开独立思考
对于当前k来说,能选到最大值的概率是 1/n*(k+1)*P+1/n*(k+2)*P+...*1/n*(n)*P的和
期望定义嘛0.0 E(ax+by)=aE(X)+bE(Y)
P是不同的,为了保证在当前i位一定能被选到,所以1到k里面的最大值要是1到i-1里面的最大值
所以P=k/(i-1)
然后就把未知数提取出来然后枚举k o(1)计算即可,要预处理1/i的前缀和
#include
using namespace std;
const int N = 2e5+10,mod=998244353;
#define int long long
typedef pair PII;
typedef long long LL;
int n,m,k;
int a[N];
double sum[N];
void init(){
for(int i=1;i>n;
int ans=0;
double p=1.0/n;
for(int i=1;i<=n-1;i++){
double now=calc(n,i);
if(now>p){
ans=i,p=now;
}
}
cout<>t;
while(t--) solve();
}
1012 | Coin |
额虽然感觉可能看不太出是网络流的题,但他确实是最大流模板题...
首先有个知识就是拆点,一个点拆成两个点
科普小知识
将每个人拆成一个入点和一个出点,原先连向它的边变成连向入点的边,原先从它连出去的边变成从出点连出去的边。然后从入点向出点连一条容量为 自身限制的流量的边,保证再多的流量流向这个人,在经过中间这条容量为都不会超过他的上限
然后可以想怎么建图了,因为一开始每个人只有一块钱,所以起点连向每个点流量的边都是1
因为要按照询问顺序连边,我们需要记录自身点上一次询问操作过的点的编号,进行拆点,再连向A,B,流量都是1,然后最后把所有点的最后顺序点的编号都连向终点即可
然后直接copy dinic模板就能过了
大概就这样图....,如果没学过网络流的话...建议直接跳了把这题
#include
using namespace std;
const int N = 3e4+10,mod=998244353,M=3*N;
typedef pair PII;
typedef long long LL;
#define S (N*2+1)
#define T (N*2+2)
#define INF 0x3f3f3f3f
int n,m,k;
int h[M],e[500010],ne[500010],f[500010],idx,pn;
int q[M],d[M],cur[M];
int p[M],a[N];
void add(int a, int b, int c,int w=0)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = w, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs(){
int hh=0,tt=0;
memset(d,-1,sizeof(d));
q[S]=0,d[S]=0,cur[S]=h[S];
q[0]=S;
while(hh<=tt){
int t=q[hh++];
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(d[j]==-1&&f[i]){
d[j]=d[t]+1;
cur[j]=h[j];
if(j==T) return true;
q[++tt]=j;
}
}
}
return false;
}
int find(int u,int limit){
if(u==T) return limit;
int flow=0;
for(int i=h[u];~i&&flow>n>>m>>k;
for(int i=1;i<=n;i++) cin>>a[i];
pn=0;
memset(p,0,sizeof(p));
memset(h,-1,sizeof(h));idx=0;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
p[x]?add(p[x],++pn,a[x]):add(S,++pn,1);
p[x]=pn;
p[y]?add(p[y],++pn,a[y]):add(S,++pn,1);
p[y]=pn;
add(p[x],p[y],1,1);
}
for(int i=1;i<=k;i++){
int x;cin>>x;
if(!p[x]) add(S,T,1);
else add(p[x],T,a[x]);
}
cout<>t;
while(t--) solve();
}