168 | team0691 北京林业大学 |
4 | 11:47:00 |
1004:
如果是求和等于p,则直接用滑动窗口。
而这里是取模,我们依然可以类比滑动窗口,用map进行快速维护。
总体思想:贪心,前面能构造出尽量构造,给后面的数多一些选择空间
#include
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
int a[M];
ll sm[M];
unordered_mapmp;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,p;
scanf("%d%d",&n,&p);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),sm[i]=(sm[i-1]+a[i])%p;
int ans=0,tp=0;
mp[0]=1;
for(int i=1;i<=n;i++)
{
if(a[i]%p==0)
{
mp.clear();
ans++;
tp=0;
mp[0]=1;
// mp[(tp+a[i])%p]=i;
continue;
}
// cout<
1005:
显然是并查集,刚开始算总数量。
然后每连接一个关系就减去一些数量。
连接两个集合x,y时:
会少:
x中一个2,和y中一个2,以及补集中一个2的队伍个数。
x中一个1,和y中一个2,以及补集中一个2的队伍个数。
x中一个2,和y中一个1,以及补集中一个2的队伍个数。
x中一个2,和y中一个2,以及补集中一个1的队伍个数。
#include
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
const int mod =1e9+7;
int fa[M];
ll sz1[M],sz2[M];
int gt(int x)
{
if(x==fa[x])return x;
return fa[x]=gt(fa[x]);
}
int a[M];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
ll nm1=0,nm2=0;
for(int i=1;i<=n;i++)fa[i]=i,sz1[i]=sz2[i]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==1)nm1++,sz1[i]=1;
else nm2++,sz2[i]=1;
}
ll ans=nm2*(nm2-1)/2%mod*nm1%mod;
if(nm2>2)ans+=nm2*(nm2-1)*(nm2-2)/6%mod;
ans%=mod;
printf("%lld\n",ans);
for(int tt=1;tt0)tp+=sz1[gx]*sz2[gy]*z%mod+sz1[gy]*sz2[gx]*z%mod+sz2[gy]*sz2[gx]*z%mod;tp%=mod;
z=nm1-sz1[gx]-sz1[gy];
if(z>0)tp+=sz2[gx]*sz2[gy]*z%mod;tp%=mod;
ans=((ans-tp)%mod+mod)%mod;
fa[gy]=gx;
sz1[gx]+=sz1[gy];
sz2[gx]+=sz2[gy];
}
printf("%lld\n",ans);
}
}
return 0;
}
1007:
暴力做法:
每次都从最短路上删一条边,删五次。
我跑了个随机数据发现:平均最短路边数为4,且只有一条。
然后直接模拟上述过程即可。
复杂度为:
O(w^5 *2500*10)
w为每次最短路的期望边数。
刚开始我是按w为10跑的,但发现随机数据甚至小于10,所以直接写即可。
一些删边/村边/打印边 小技巧都在代码里了。
#include
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const int N = 50+7;
const int M = 4*50*50+7;
int head[N],cnt=1;
int idu[M],idv[M];
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){
ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;
idu[cnt]=x;
idv[cnt]=y;
//cout<<" =====" <v[7];
int mp[M],vv[N];
void gt(int x,int ty)
{
vv[x]=1;
// cout<
1009
括号序列经典问题:
只要一个括号序列任意前缀和大于0,且最后所有和等于0.( 注意'('=1.')'=-1 )
这一题要求最短,且字典序最小。
就是说,在满足它是个括号序列的基本条件下,(尽量在左边,)尽量在右边。
所以我们直接第一遍加(,满足任意前缀大于0,尽量在前面加左括号,用min(i+1,n)来查找后面是否都满足条件。
每次更新前缀和。由于更新累加,可以直接记录差值,O1的进行查询。
最后从后往前加')',满足所有和等于0.
最后再判断一次是否满足括号序列即可。
上述做法正确性显然:
1:左括号加的尽量少,且尽量在左边。
2:右括号加的尽量少,且尽量在右边。
如果最后依然不满足,则一定不能构造出正解
#include
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
char s[M];
int a[M];
int mn[M];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
bool flag=true;
int n=strlen(s+1);
for(int i=1;i<=n;i++)
{
a[i]=a[i-1];
if(s[i]=='(')a[i]++;
else if(s[i]==')')a[i]--;
}
mn[n+1]=n+1;
for(int i=n;i>=1;i--)
mn[i]=min(mn[i+1],a[i]);
int det=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='*')
{
if(mn[i+1]+det<0)
{
s[i]='(';
det++;
}
}
if(a[i]+det<0)flag=false;
}
if(!flag)
{
puts("No solution!");
continue;
}
int nm=a[n]+det;
for(int i=n;i>=1;i--)
{
if(s[i]=='*'&&nm>0)
{
nm--;
s[i]=')';
}
}
int tp=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='(')tp++;
else if(s[i]==')')tp--;
if(tp<0)flag=false;
}
if(tp!=0)flag=false;
if(!flag)puts("No solution!");
else
{
for(int i=1;i<=n;i++)if(s[i]!='*')printf("%c",s[i]);
puts("");
}
}
return 0;
}